LCOV - code coverage report
Current view: top level - src/llmq - signing_shares.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 866 945 91.6 %
Date: 2026-06-25 07:23:43 Functions: 66 71 93.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <llmq/signing_shares.h>
       6             : 
       7             : #include <active/masternode.h>
       8             : #include <chainparams.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <llmq/commitment.h>
      11             : #include <llmq/options.h>
      12             : #include <llmq/quorums.h>
      13             : #include <llmq/quorumsman.h>
      14             : #include <llmq/signhash.h>
      15             : #include <llmq/signing.h>
      16             : #include <netmessagemaker.h>
      17             : #include <util/helpers.h>
      18             : #include <util/std23.h>
      19             : #include <util/thread.h>
      20             : #include <util/time.h>
      21             : #include <validation.h>
      22             : 
      23             : #include <cxxtimer.hpp>
      24             : 
      25             : #include <ranges>
      26             : 
      27             : namespace llmq
      28             : {
      29       43386 : void CSigShare::UpdateKey()
      30             : {
      31       43386 :     key.first = this->buildSignHash().Get();
      32       43386 :     key.second = quorumMember;
      33       43386 : }
      34             : 
      35       21816 : std::string CSigSesAnn::ToString() const
      36             : {
      37       21816 :     return strprintf("sessionId=%d, llmqType=%d, quorumHash=%s, id=%s, msgHash=%s",
      38       21816 :                      sessionId, std23::to_underlying(getLlmqType()), getQuorumHash().ToString(), getId().ToString(), getMsgHash().ToString());
      39           0 : }
      40             : 
      41       26966 : void CSigSharesInv::Merge(const CSigSharesInv& inv2)
      42             : {
      43      138580 :     for (const auto i : util::irange(inv.size())) {
      44      111614 :         if (inv2.inv[i]) {
      45       31638 :             inv[i] = inv2.inv[i];
      46       31638 :         }
      47             :     }
      48       26966 : }
      49             : 
      50       15346 : size_t CSigSharesInv::CountSet() const
      51             : {
      52       15346 :     return (size_t)std::count(inv.begin(), inv.end(), true);
      53             : }
      54             : 
      55       67115 : std::string CSigSharesInv::ToString() const
      56             : {
      57       67115 :     std::string str = "(";
      58       67115 :     bool first = true;
      59    15197227 :     for (const auto i : util::irange(inv.size())) {
      60    15129820 :         if (!inv[i]) {
      61    15058886 :             continue;
      62             :         }
      63             : 
      64       71226 :         if (!first) {
      65        4845 :             str += ",";
      66        4845 :         }
      67       71226 :         first = false;
      68       71226 :         str += strprintf("%d", i);
      69             :     }
      70       66380 :     str += ")";
      71       66381 :     return str;
      72       67849 : }
      73             : 
      74      150693 : void CSigSharesInv::Init(size_t size)
      75             : {
      76      150693 :     inv.resize(size, false);
      77      150693 : }
      78             : 
      79       41368 : void CSigSharesInv::Set(uint16_t quorumMember, bool v)
      80             : {
      81       41368 :     assert(quorumMember < inv.size());
      82       41368 :     inv[quorumMember] = v;
      83       41368 : }
      84             : 
      85          97 : void CSigSharesInv::SetAll(bool v)
      86             : {
      87          97 :     std::fill(inv.begin(), inv.end(), v);
      88          97 : }
      89             : 
      90       37547 : std::string CBatchedSigShares::ToInvString() const
      91             : {
      92       37547 :     CSigSharesInv inv;
      93             :     // we use 400 here no matter what the real size is. We don't really care about that size as we just want to call ToString()
      94       37547 :     inv.Init(400);
      95       75140 :     for (const auto& sigShare : sigShares) {
      96       37593 :         inv.inv[sigShare.first] = true;
      97             :     }
      98       37547 :     return inv.ToString();
      99       37547 : }
     100             : 
     101       32599 : static void InitSession(CSigSharesNodeState::Session& s, const llmq::SignHash& signHash, CSigBase from)
     102             : {
     103       32599 :     const auto& llmq_params_opt = Params().GetLLMQ(from.getLlmqType());
     104       32599 :     assert(llmq_params_opt.has_value());
     105       32599 :     const auto& llmq_params = llmq_params_opt.value();
     106             : 
     107       32599 :     s.llmqType = from.getLlmqType();
     108       32599 :     s.quorumHash = from.getQuorumHash();
     109       32599 :     s.id = from.getId();
     110       32599 :     s.msgHash = from.getMsgHash();
     111       32599 :     s.signHash = signHash;
     112       32599 :     s.announced.Init((size_t)llmq_params.size);
     113       32599 :     s.requested.Init((size_t)llmq_params.size);
     114       32599 :     s.knows.Init((size_t)llmq_params.size);
     115       32599 : }
     116             : 
     117       53057 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromShare(const llmq::CSigShare& sigShare)
     118             : {
     119       53057 :     auto& s = sessions[sigShare.GetSignHash()];
     120       53057 :     if (s.announced.inv.empty()) {
     121       21689 :         InitSession(s, sigShare.buildSignHash(), sigShare);
     122       21689 :     }
     123       53057 :     return s;
     124             : }
     125             : 
     126       21816 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromAnn(const llmq::CSigSesAnn& ann)
     127             : {
     128       21816 :     auto signHash = ann.buildSignHash();
     129       21816 :     auto& s = sessions[signHash.Get()];
     130       21816 :     if (s.announced.inv.empty()) {
     131       10910 :         InitSession(s, signHash, ann);
     132       10910 :     }
     133       21816 :     return s;
     134             : }
     135             : 
     136       36271 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionBySignHash(const uint256& signHash)
     137             : {
     138       36271 :     auto it = sessions.find(signHash);
     139       36271 :     if (it == sessions.end()) {
     140        1904 :         return nullptr;
     141             :     }
     142       34367 :     return &it->second;
     143       36271 : }
     144             : 
     145       47714 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionByRecvId(uint32_t sessionId)
     146             : {
     147       47714 :     auto it = sessionByRecvId.find(sessionId);
     148       47714 :     if (it == sessionByRecvId.end()) {
     149        1284 :         return nullptr;
     150             :     }
     151       46430 :     return it->second;
     152       47714 : }
     153             : 
     154       34226 : bool CSigSharesNodeState::GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo)
     155             : {
     156       34226 :     const auto* s = GetSessionByRecvId(sessionId);
     157       34226 :     if (s == nullptr) {
     158        1279 :         return false;
     159             :     }
     160       32947 :     retInfo.llmqType = s->llmqType;
     161       32947 :     retInfo.quorumHash = s->quorumHash;
     162       32947 :     retInfo.id = s->id;
     163       32947 :     retInfo.msgHash = s->msgHash;
     164       32947 :     retInfo.signHash = s->signHash;
     165       32947 :     retInfo.quorum = s->quorum;
     166             : 
     167       32947 :     return true;
     168       34226 : }
     169             : 
     170       88724 : void CSigSharesNodeState::RemoveSession(const uint256& signHash)
     171             : {
     172       88724 :     if (const auto it = sessions.find(signHash); it != sessions.end()) {
     173       30923 :         sessionByRecvId.erase(it->second.recvSessionId);
     174       30923 :         sessions.erase(it);
     175       30923 :     }
     176       88724 :     requestedSigShares.EraseAllForSignHash(signHash);
     177       88724 :     pendingIncomingSigShares.EraseAllForSignHash(signHash);
     178       88724 : }
     179             : 
     180             : //////////////////////
     181             : 
     182        2640 : CSigSharesManager::CSigSharesManager(CConnman& connman, const ChainstateManager& chainman, CSigningManager& _sigman,
     183             :                                      const CActiveMasternodeManager& mn_activeman, const CQuorumManager& _qman,
     184             :                                      const CSporkManager& sporkman) :
     185         660 :     m_connman{connman},
     186         660 :     m_chainman{chainman},
     187         660 :     sigman{_sigman},
     188         660 :     m_mn_activeman{mn_activeman},
     189         660 :     qman{_qman},
     190         660 :     m_sporkman{sporkman}
     191        1320 : {
     192         660 : }
     193             : 
     194        1980 : CSigSharesManager::~CSigSharesManager() = default;
     195             : 
     196         660 : void CSigSharesManager::RegisterRecoveryInterface()
     197             : {
     198         660 :     sigman.RegisterRecoveredSigsListener(this);
     199         660 : }
     200             : 
     201         660 : void CSigSharesManager::UnregisterRecoveryInterface()
     202             : {
     203         660 :     sigman.UnregisterRecoveredSigsListener(this);
     204         660 : }
     205             : 
     206       21816 : bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann)
     207             : {
     208       21816 :     auto llmqType = ann.getLlmqType();
     209       21816 :     if (!Params().GetLLMQ(llmqType).has_value()) {
     210           0 :         return false;
     211             :     }
     212       21816 :     if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) {
     213           0 :         return false;
     214             :     }
     215             : 
     216       21816 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- ann={%s}, node=%d\n", __func__, ann.ToString(), pfrom.GetId());
     217             : 
     218       21816 :     auto quorum = qman.GetQuorum(llmqType, ann.getQuorumHash());
     219       21816 :     if (!quorum) {
     220             :         // TODO should we ban here?
     221           0 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorum %s not found, node=%d\n", __func__,
     222             :                   ann.getQuorumHash().ToString(), pfrom.GetId());
     223           0 :         return true; // let's still try other announcements from the same message
     224             :     }
     225             : 
     226       21816 :     LOCK(cs);
     227       21816 :     auto& nodeState = nodeStates[pfrom.GetId()];
     228       21816 :     auto& session = nodeState.GetOrCreateSessionFromAnn(ann);
     229       21816 :     nodeState.sessionByRecvId.erase(session.recvSessionId);
     230       21816 :     nodeState.sessionByRecvId.erase(ann.getSessionId());
     231       21816 :     session.recvSessionId = ann.getSessionId();
     232       21816 :     session.quorum = quorum;
     233       21816 :     nodeState.sessionByRecvId.try_emplace(ann.getSessionId(), &session);
     234             : 
     235       21816 :     return true;
     236       21816 : }
     237             : 
     238       14226 : static bool VerifySigSharesInv(Consensus::LLMQType llmqType, const CSigSharesInv& inv)
     239             : {
     240       14226 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     241       14226 :     return llmq_params_opt.has_value() && (inv.inv.size() == size_t(llmq_params_opt->size));
     242             : }
     243             : 
     244       15333 : bool CSigSharesManager::ProcessMessageSigShares(const CNode& pfrom, const CSigSharesInv& inv, const std::string& msg_type)
     245             : {
     246       15333 :     CSigSharesNodeState::SessionInfo sessionInfo;
     247       15333 :     if (!GetSessionInfoByRecvId(pfrom.GetId(), inv.sessionId, sessionInfo)) {
     248        1107 :         return true;
     249             :     }
     250             : 
     251       14226 :     if (!VerifySigSharesInv(sessionInfo.llmqType, inv)) {
     252           0 :         return false;
     253             :     }
     254             : 
     255             :     // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
     256       14226 :     if (sigman.HasRecoveredSigForSession(sessionInfo.signHash.Get())) {
     257         738 :         return true;
     258             :     }
     259             : 
     260       13488 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, inv={%s}, node=%d\n", __func__,
     261             :             sessionInfo.signHash.ToString(), inv.ToString(), pfrom.GetId());
     262             : 
     263       13488 :     if (msg_type == NetMsgType::QSIGSHARESINV && !sessionInfo.quorum->HasVerificationVector()) {
     264             :         // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
     265           0 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have the quorum vvec for %s, not requesting sig shares. node=%d\n", __func__,
     266             :                   sessionInfo.quorumHash.ToString(), pfrom.GetId());
     267           0 :         return true;
     268             :     }
     269             : 
     270       13488 :     LOCK(cs);
     271       13488 :     auto& nodeState = nodeStates[pfrom.GetId()];
     272       13488 :     auto* session = nodeState.GetSessionByRecvId(inv.sessionId);
     273       13488 :     if (session == nullptr) {
     274           5 :         return true;
     275             :     }
     276       13483 :     if (msg_type == NetMsgType::QSIGSHARESINV) {
     277       10688 :         session->announced.Merge(inv);
     278       10688 :     } else { // msg_type == NetMsgType::QGETSIGSHARES
     279        2795 :         session->requested.Merge(inv);
     280             :     }
     281             : 
     282       13483 :     session->knows.Merge(inv);
     283       13483 :     return true;
     284       15333 : }
     285             : 
     286             : // Failure is not issue, we should not ban node
     287       25100 : static bool PreVerifySigShareQuorum(const CActiveMasternodeManager& mn_activeman, const CQuorumManager& quorum_manager,
     288             :                                     const CQuorumCPtr& quorum, Consensus::LLMQType llmqType)
     289             : {
     290       25100 :     if (!IsQuorumActive(llmqType, quorum_manager, quorum->qc->quorumHash)) {
     291             :         // quorum is too old
     292           0 :         return false;
     293             :     }
     294       25100 :     if (!quorum->IsMember(mn_activeman.GetProTxHash())) {
     295             :         // we're not a member so we can't verify it (we actually shouldn't have received it)
     296         179 :         return false;
     297             :     }
     298       24921 :     if (!quorum->HasVerificationVector()) {
     299             :         // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
     300         131 :         LogPrint(BCLog::LLMQ_SIGS, "%s -- we don't have the quorum vvec for %s, no verification possible.\n", __func__,
     301             :                  quorum->qc->quorumHash.ToString());
     302         131 :         return false;
     303             :     }
     304       24790 :     return true;
     305       25100 : }
     306             : 
     307             : // Ban node if PreVerifyBatchedSigShares failed
     308       18631 : bool PreVerifyBatchedSigShares(const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares)
     309             : {
     310       18631 :     std::unordered_set<uint16_t> dupMembers;
     311             : 
     312       55935 :     for (const auto& [quorumMember, _] : batchedSigShares.sigShares) {
     313       18652 :         if (!dupMembers.emplace(quorumMember).second) {
     314           0 :             return false;
     315             :         }
     316             : 
     317       18652 :         if (quorumMember >= session.quorum->members.size()) {
     318           0 :             LogPrint(BCLog::LLMQ_SIGS, "%s -- quorumMember out of bounds\n", __func__);
     319           0 :             return false;
     320             :         }
     321       37304 :         if (!session.quorum->qc->validMembers[quorumMember]) {
     322           0 :             LogPrint(BCLog::LLMQ_SIGS, "%s -- quorumMember not valid\n", __func__);
     323           0 :             return false;
     324             :         }
     325             :     }
     326       18631 :     return true;
     327       18631 : }
     328             : 
     329       18893 : bool CSigSharesManager::ProcessMessageBatchedSigShares(const CNode& pfrom, const CBatchedSigShares& batchedSigShares)
     330             : {
     331       18893 :     CSigSharesNodeState::SessionInfo sessionInfo;
     332       18893 :     if (!GetSessionInfoByRecvId(pfrom.GetId(), batchedSigShares.sessionId, sessionInfo)) {
     333         172 :         return true;
     334             :     }
     335             : 
     336       18721 :     if (!PreVerifySigShareQuorum(m_mn_activeman, qman, sessionInfo.quorum, sessionInfo.llmqType)) {
     337          90 :         return true;
     338             :     }
     339             : 
     340       18631 :     if (!PreVerifyBatchedSigShares(sessionInfo, batchedSigShares)) {
     341           0 :         return false; // ban node
     342             :     }
     343             : 
     344       18631 :     std::vector<CSigShare> sigSharesToProcess;
     345       18631 :     sigSharesToProcess.reserve(batchedSigShares.sigShares.size());
     346             : 
     347             :     {
     348       18631 :         LOCK(cs);
     349       18631 :         auto& nodeState = nodeStates[pfrom.GetId()];
     350             : 
     351       37283 :         for (const auto& sigSharetmp : batchedSigShares.sigShares) {
     352       18652 :             CSigShare sigShare = RebuildSigShare(sessionInfo, sigSharetmp);
     353       18652 :             nodeState.requestedSigShares.Erase(sigShare.GetKey());
     354             : 
     355             :             // TODO track invalid sig shares received for PoSe?
     356             :             // It's important to only skip seen *valid* sig shares here. If a node sends us a
     357             :             // batch of mostly valid sig shares with a single invalid one and thus batched
     358             :             // verification fails, we'd skip the valid ones in the future if received from other nodes
     359       18652 :             if (sigShares.Has(sigShare.GetKey())) {
     360          14 :                 continue;
     361             :             }
     362             : 
     363             :             // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
     364       18638 :             if (sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId())) {
     365         396 :                 continue;
     366             :             }
     367             : 
     368       18242 :             sigSharesToProcess.emplace_back(sigShare);
     369       18652 :         }
     370       18631 :     }
     371             : 
     372       18631 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, shares=%d, new=%d, inv={%s}, node=%d\n", __func__,
     373             :              sessionInfo.signHash.ToString(), batchedSigShares.sigShares.size(), sigSharesToProcess.size(), batchedSigShares.ToInvString(), pfrom.GetId());
     374             : 
     375       18631 :     if (sigSharesToProcess.empty()) {
     376         405 :         return true;
     377             :     }
     378             : 
     379       18226 :     LOCK(cs);
     380       18226 :     auto& nodeState = nodeStates[pfrom.GetId()];
     381       36468 :     for (const auto& s : sigSharesToProcess) {
     382       18242 :         nodeState.pendingIncomingSigShares.Add(s.GetKey(), s);
     383             :     }
     384       18226 :     return true;
     385       18893 : }
     386             : 
     387        6379 : bool CSigSharesManager::ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare)
     388             : {
     389        6379 :     auto quorum = qman.GetQuorum(sigShare.getLlmqType(), sigShare.getQuorumHash());
     390        6379 :     if (!quorum) {
     391           0 :         return true;
     392             :     }
     393        6379 :     if (!PreVerifySigShareQuorum(m_mn_activeman, qman, quorum, sigShare.getLlmqType())) {
     394         220 :         return true;
     395             :     }
     396             : 
     397        6159 :     if (sigShare.getQuorumMember() >= quorum->members.size()) {
     398           0 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember out of bounds\n", __func__);
     399           0 :         return false;
     400             :     }
     401        6159 :     if (!quorum->qc->validMembers[sigShare.getQuorumMember()]) {
     402           0 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember not valid\n", __func__);
     403           0 :         return false;
     404             :     }
     405             : 
     406        6159 :     const auto signHash = sigShare.GetSignHash();
     407       11559 :     const bool alreadyRecovered = sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId()) ||
     408        5400 :                                   sigman.HasRecoveredSigForSession(signHash);
     409             : 
     410             :     {
     411        6159 :         LOCK(cs);
     412             : 
     413        6159 :         if (alreadyRecovered) {
     414         759 :             LogPrint(BCLog::LLMQ_SIGS, /* Continued */
     415             :                      "CSigSharesManager::%s -- dropping sigShare for recovered session. signHash=%s, id=%s, "
     416             :                      "msgHash=%s, member=%d, node=%d\n",
     417             :                      __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(),
     418             :                      sigShare.getQuorumMember(), fromId);
     419         759 :             return true;
     420             :         }
     421             : 
     422        5400 :         if (sigShares.Has(sigShare.GetKey())) {
     423           0 :             return true;
     424             :         }
     425             : 
     426        5400 :         auto& nodeState = nodeStates[fromId];
     427        5400 :         nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
     428        6159 :     }
     429             : 
     430        5400 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, id=%s, msgHash=%s, member=%d, node=%d\n", __func__,
     431             :              signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), sigShare.getQuorumMember(), fromId);
     432        5400 :     return true;
     433        6379 : }
     434             : 
     435      708919 : bool CSigSharesManager::CollectPendingSigSharesToVerify(
     436             :     size_t maxUniqueSessions, std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
     437             :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
     438             : {
     439      708919 :     bool more_work{false};
     440             : 
     441             :     {
     442      708919 :         LOCK(cs);
     443      708919 :         if (nodeStates.empty()) {
     444      416847 :             return false;
     445             :         }
     446             : 
     447             :         // This will iterate node states in random order and pick one sig share at a time. This avoids processing
     448             :         // of large batches at once from the same node while other nodes also provided shares. If we wouldn't do this,
     449             :         // other nodes would be able to poison us with a large batch with N-1 valid shares and the last one being
     450             :         // invalid, making batch verification fail and revert to per-share verification, which in turn would slow down
     451             :         // the whole verification process
     452      292072 :         std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
     453      292072 :         IterateNodesRandom(
     454      292072 :             nodeStates,
     455     1214592 :             [&]() {
     456      922520 :                 return uniqueSignHashes.size() < maxUniqueSessions;
     457             :                 // TODO: remove NO_THREAD_SAFETY_ANALYSIS
     458             :                 // using here template IterateNodesRandom makes impossible to use lock annotation
     459             :             },
     460     1214584 :             [&](NodeId nodeId, CSigSharesNodeState& ns) NO_THREAD_SAFETY_ANALYSIS {
     461      922512 :                 if (ns.pendingIncomingSigShares.Empty()) {
     462      899672 :                     return false;
     463             :                 }
     464       22840 :                 const auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
     465             : 
     466       22840 :                 AssertLockHeld(cs);
     467       22840 :                 if (const bool alreadyHave = this->sigShares.Has(sigShare.GetKey()); !alreadyHave) {
     468       22840 :                     uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
     469       22840 :                     retSigShares[nodeId].emplace_back(sigShare);
     470       22840 :                 }
     471       22840 :                 ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
     472       22840 :                 return !ns.pendingIncomingSigShares.Empty();
     473      922512 :             },
     474      292072 :             rnd);
     475             : 
     476      292072 :         if (retSigShares.empty()) {
     477      279890 :             return false;
     478             :         }
     479             : 
     480             :         // Determine if there is still work left in any node state after pulling this batch
     481       12182 :         more_work = std::any_of(nodeStates.begin(), nodeStates.end(),
     482       41825 :                                 [](const auto& entry) {
     483       41825 :                                     const auto& ns = entry.second;
     484       41825 :                                     return !ns.pendingIncomingSigShares.Empty();
     485             :                                 });
     486      708919 :     }
     487             : 
     488             :     // For the convenience of the caller, also build a map of quorumHash -> quorum
     489             : 
     490       27572 :     for (const auto& [_, vecSigShares] : retSigShares) {
     491       38230 :         for (const auto& sigShare : vecSigShares) {
     492       22840 :             auto llmqType = sigShare.getLlmqType();
     493             : 
     494       22840 :             auto k = std::make_pair(llmqType, sigShare.getQuorumHash());
     495       22840 :             if (retQuorums.count(k) != 0) {
     496        9571 :                 continue;
     497             :             }
     498             : 
     499       13269 :             auto quorum = qman.GetQuorum(llmqType, sigShare.getQuorumHash());
     500             :             // Despite constructing a convenience map, we assume that the quorum *must* be present.
     501             :             // The absence of it might indicate an inconsistent internal state, so we should report
     502             :             // nothing instead of reporting flawed data.
     503       13269 :             if (!quorum) {
     504           0 :                 LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, quorumHash=%s\n", __func__,
     505             :                           std23::to_underlying(llmqType), sigShare.getQuorumHash().ToString());
     506           0 :                 return false;
     507             :             }
     508       13269 :             retQuorums.try_emplace(k, quorum);
     509       13269 :         }
     510             :     }
     511             : 
     512       12182 :     return more_work;
     513      708919 : }
     514             : 
     515             : // It's ensured that no duplicates are passed to this method
     516       15386 : std::vector<std::shared_ptr<CRecoveredSig>> CSigSharesManager::ProcessPendingSigShares(
     517             :     const std::vector<CSigShare>& sigSharesToProcess,
     518             :     const std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& quorums)
     519             : {
     520       15386 :     cxxtimer::Timer t(true);
     521       15386 :     std::vector<std::shared_ptr<CRecoveredSig>> recovered_sigs;
     522       38222 :     for (const auto& sigShare : sigSharesToProcess) {
     523       22836 :         auto quorumKey = std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash());
     524       22836 :         auto rs = ProcessSigShare(sigShare, quorums.at(quorumKey));
     525       22836 :         if (rs != nullptr) {
     526        8464 :             recovered_sigs.emplace_back(std::move(rs));
     527        8464 :         }
     528       22836 :     }
     529       15386 :     t.stop();
     530             : 
     531       15386 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- processed sigShare batch. shares=%d, time=%ds\n", __func__,
     532             :              sigSharesToProcess.size(), t.count());
     533       15386 :     return recovered_sigs;
     534       15386 : }
     535             : 
     536             : // sig shares are already verified when entering this method
     537       41194 : std::shared_ptr<CRecoveredSig> CSigSharesManager::ProcessSigShare(const CSigShare& sigShare, const CQuorumCPtr& quorum)
     538             : {
     539       41194 :     auto llmqType = quorum->params.type;
     540             : 
     541       41194 :     const bool isAllMembersConnectedEnabled = IsAllMembersConnectedEnabled(llmqType, m_sporkman);
     542             : 
     543             :     // prepare node set for direct-push in case this is our sig share
     544       41194 :     std::vector<NodeId> quorumNodes;
     545       70084 :     if (!isAllMembersConnectedEnabled &&
     546       28890 :         sigShare.getQuorumMember() == quorum->GetMemberIndex(m_mn_activeman.GetProTxHash())) {
     547       11279 :         quorumNodes = m_connman.GetMasternodeQuorumNodes(sigShare.getLlmqType(), sigShare.getQuorumHash());
     548       11279 :     }
     549             : 
     550       41194 :     if (sigman.HasRecoveredSigForId(llmqType, sigShare.getId())) {
     551        1483 :         return nullptr;
     552             :     }
     553             : 
     554       39711 :     bool canTryRecovery = false;
     555             :     {
     556       39711 :         LOCK(cs);
     557             : 
     558       39711 :         if (!sigShares.Add(sigShare.GetKey(), sigShare)) {
     559          44 :             return nullptr;
     560             :         }
     561       39667 :         if (!isAllMembersConnectedEnabled) {
     562       27974 :             sigSharesQueuedToAnnounce.Add(sigShare.GetKey(), true);
     563       27974 :         }
     564             : 
     565             :         // Update the time we've seen the last sigShare
     566       39667 :         timeSeenForSessions[sigShare.GetSignHash()] = GetTime<std::chrono::seconds>().count();
     567             : 
     568             :         // don't announce and wait for other nodes to request this share and directly send it to them
     569             :         // there is no way the other nodes know about this share as this is the one created on this node
     570       60351 :         for (auto otherNodeId : quorumNodes) {
     571       20684 :             auto& nodeState = nodeStates[otherNodeId];
     572       20684 :             auto& session = nodeState.GetOrCreateSessionFromShare(sigShare);
     573       20684 :             session.quorum = quorum;
     574       20684 :             session.requested.Set(sigShare.getQuorumMember(), true);
     575       20684 :             session.knows.Set(sigShare.getQuorumMember(), true);
     576             :         }
     577             : 
     578       39667 :         size_t sigShareCount = sigShares.CountForSignHash(sigShare.GetSignHash());
     579       39667 :         if (sigShareCount >= size_t(quorum->params.threshold)) {
     580        8782 :             canTryRecovery = true;
     581        8782 :         }
     582       39711 :     }
     583       39667 :     if (!canTryRecovery) return nullptr;
     584             : 
     585        8782 :     return TryRecoverSig(*quorum, sigShare.getId(), sigShare.getMsgHash());
     586       41196 : }
     587             : 
     588        8782 : std::shared_ptr<CRecoveredSig> CSigSharesManager::TryRecoverSig(const CQuorum& quorum, const uint256& id,
     589             :                                                                 const uint256& msgHash)
     590             : {
     591        8782 :     if (sigman.HasRecoveredSigForId(quorum.params.type, id)) {
     592           0 :         return nullptr;
     593             :     }
     594             : 
     595        8782 :     std::vector<CBLSSignature> sigSharesForRecovery;
     596        8782 :     std::vector<CBLSId> idsForRecovery;
     597             :     {
     598        8782 :         LOCK(cs);
     599             : 
     600        8782 :         auto signHash = SignHash(quorum.params.type, quorum.qc->quorumHash, id, msgHash).Get();
     601        8782 :         const auto* sigSharesForSignHash = sigShares.GetAllForSignHash(signHash);
     602        8782 :         if (sigSharesForSignHash == nullptr) {
     603           0 :             return nullptr;
     604             :         }
     605             : 
     606        8782 :         std::shared_ptr<CRecoveredSig> singleMemberRecoveredSig;
     607        8782 :         if (quorum.params.is_single_member()) {
     608         241 :             if (sigSharesForSignHash->empty()) {
     609           0 :                 LogPrint(BCLog::LLMQ_SIGS, /* Continued */
     610             :                          "CSigSharesManager::%s -- impossible to recover single-node signature - no shares yet. id=%s, "
     611             :                          "msgHash=%s\n",
     612             :                          __func__, id.ToString(), msgHash.ToString());
     613           0 :                 return nullptr;
     614             :             }
     615         241 :             const auto& sigShare = sigSharesForSignHash->begin()->second;
     616         241 :             CBLSSignature recoveredSig = sigShare.sigShare.Get();
     617         241 :             LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recover single-node signature. id=%s, msgHash=%s\n",
     618             :                      __func__, id.ToString(), msgHash.ToString());
     619             : 
     620         241 :             singleMemberRecoveredSig = std::make_shared<CRecoveredSig>(quorum.params.type, quorum.qc->quorumHash, id, msgHash,
     621             :                                                       recoveredSig);
     622         241 :         }
     623             : 
     624        8782 :         sigSharesForRecovery.reserve((size_t) quorum.params.threshold);
     625        8782 :         idsForRecovery.reserve((size_t) quorum.params.threshold);
     626       31438 :         for (auto it = sigSharesForSignHash->begin(); it != sigSharesForSignHash->end() && sigSharesForRecovery.size() < size_t(quorum.params.threshold); ++it) {
     627       22656 :             const auto& sigShare = it->second;
     628       22656 :             sigSharesForRecovery.emplace_back(sigShare.sigShare.Get());
     629       22656 :             idsForRecovery.emplace_back(quorum.members[sigShare.getQuorumMember()]->proTxHash);
     630       22656 :         }
     631             : 
     632             :         // check if we can recover the final signature
     633        8782 :         if (sigSharesForRecovery.size() < size_t(quorum.params.threshold)) {
     634           0 :             return nullptr;
     635             :         }
     636        8782 :         if (quorum.params.is_single_member()) {
     637         241 :             return singleMemberRecoveredSig; // end of single-quorum processing
     638             :         }
     639        8782 :     }
     640             : 
     641             :     // now recover it
     642        8541 :     cxxtimer::Timer t(true);
     643        8541 :     CBLSSignature recoveredSig;
     644        8541 :     if (!recoveredSig.Recover(sigSharesForRecovery, idsForRecovery)) {
     645           5 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- failed to recover signature. id=%s, msgHash=%s, time=%d\n", __func__,
     646             :                   id.ToString(), msgHash.ToString(), t.count());
     647           5 :         return nullptr;
     648             :     }
     649             : 
     650        8536 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recovered signature. id=%s, msgHash=%s, time=%d\n", __func__,
     651             :               id.ToString(), msgHash.ToString(), t.count());
     652             : 
     653        8536 :     auto rs = std::make_shared<CRecoveredSig>(quorum.params.type, quorum.qc->quorumHash, id, msgHash, recoveredSig);
     654             : 
     655             :     // There should actually be no need to verify the self-recovered signatures as it should always succeed. Let's
     656             :     // however still verify it from time to time, so that we have a chance to catch bugs. We do only this sporadic
     657             :     // verification because this is unbatched and thus slow verification that happens here.
     658        8536 :     if (((recoveredSigsCounter++) % 100) == 0) {
     659         394 :         auto signHash = rs->buildSignHash();
     660         394 :         bool valid = recoveredSig.VerifyInsecure(quorum.qc->quorumPublicKey, signHash.Get());
     661         394 :         if (!valid) {
     662             :             // this should really not happen as we have verified all signature shares before
     663           0 :             LogPrintf("CSigSharesManager::%s -- own recovered signature is invalid. id=%s, msgHash=%s\n", __func__,
     664             :                       id.ToString(), msgHash.ToString());
     665           0 :             return nullptr;
     666             :         }
     667         394 :     }
     668        8536 :     return rs;
     669        8782 : }
     670             : 
     671        8726 : CDeterministicMNCPtr CSigSharesManager::SelectMemberForRecovery(const CQuorum& quorum, const uint256 &id, int attempt)
     672             : {
     673        8726 :     assert(attempt < quorum.params.recoveryMembers);
     674             : 
     675        8726 :     std::vector<std::pair<uint256, CDeterministicMNCPtr>> v;
     676        8726 :     v.reserve(quorum.members.size());
     677       47831 :     for (const auto& dmn : quorum.members) {
     678       39105 :         auto h = ::SerializeHash(std::make_pair(dmn->proTxHash, id));
     679       39105 :         v.emplace_back(h, dmn);
     680             :     }
     681        8726 :     std::sort(v.begin(), v.end());
     682             : 
     683        8726 :     return v[attempt % v.size()].second;
     684        8726 : }
     685             : 
     686       49103 : bool CSigSharesManager::AsyncSignIfMember(Consensus::LLMQType llmqType, CSigningManager& sigman, const uint256& id,
     687             :                                           const uint256& msgHash, const uint256& quorumHash, bool allowReSign,
     688             :                                           bool allowDiffMsgHashSigning)
     689             : {
     690       49103 :     AssertLockNotHeld(cs_pendingSigns);
     691             : 
     692       49103 :     if (m_mn_activeman.GetProTxHash().IsNull()) return false;
     693             : 
     694       95508 :     auto quorum = [&]() {
     695       47754 :         if (quorumHash.IsNull()) {
     696             :             // This might end up giving different results on different members
     697             :             // This might happen when we are on the brink of confirming a new quorum
     698             :             // This gives a slight risk of not getting enough shares to recover a signature
     699             :             // But at least it shouldn't be possible to get conflicting recovered signatures
     700             :             // TODO fix this by re-signing when the next block arrives, but only when that block results in a change of
     701             :             // the quorum list and no recovered signature has been created in the mean time
     702       43409 :             const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     703       43409 :             assert(llmq_params_opt.has_value());
     704       43409 :             return SelectQuorumForSigning(llmq_params_opt.value(), m_chainman.ActiveChain(), qman, id);
     705             :         } else {
     706        4345 :             return qman.GetQuorum(llmqType, quorumHash);
     707             :         }
     708       47754 :     }();
     709             : 
     710       47754 :     if (!quorum) {
     711       14020 :         LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__,
     712             :                  id.ToString(), msgHash.ToString());
     713       14020 :         return false;
     714             :     }
     715             : 
     716       33734 :     if (!quorum->IsValidMember(m_mn_activeman.GetProTxHash())) {
     717       14250 :         return false;
     718             :     }
     719             : 
     720             :     {
     721       19484 :         auto& db = sigman.GetDb();
     722       19484 :         bool hasVoted = db.HasVotedOnId(llmqType, id);
     723       19484 :         if (hasVoted) {
     724        1129 :             uint256 prevMsgHash;
     725        1129 :             db.GetVoteForId(llmqType, id, prevMsgHash);
     726        1129 :             if (msgHash != prevMsgHash) {
     727          16 :                 if (allowDiffMsgHashSigning) {
     728           8 :                     LogPrintf("%s -- already voted for id=%s and msgHash=%s. Signing for different " /* Continued */
     729             :                               "msgHash=%s\n",
     730             :                               __func__, id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
     731           8 :                     hasVoted = false;
     732           8 :                 } else {
     733           8 :                     LogPrintf("%s -- already voted for id=%s and msgHash=%s. Not voting on " /* Continued */
     734             :                               "conflicting msgHash=%s\n",
     735             :                               __func__, id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
     736           8 :                     return false;
     737             :                 }
     738        1121 :             } else if (allowReSign) {
     739          97 :                 LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
     740             :                          id.ToString(), prevMsgHash.ToString());
     741          97 :             } else {
     742        1016 :                 LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
     743             :                          id.ToString(), prevMsgHash.ToString());
     744        1016 :                 return false;
     745             :             }
     746         105 :         }
     747             : 
     748       18460 :         if (db.HasRecoveredSigForId(llmqType, id)) {
     749             :             // no need to sign it if we already have a recovered sig
     750          41 :             return true;
     751             :         }
     752       18419 :         if (!hasVoted) {
     753       18322 :             db.WriteVoteForId(llmqType, id, msgHash);
     754       18322 :         }
     755             :     }
     756             : 
     757       18419 :     if (allowReSign) {
     758             :         // make us re-announce all known shares (other nodes might have run into a timeout)
     759         887 :         ForceReAnnouncement(*quorum, llmqType, id, msgHash);
     760         887 :     }
     761       18419 :     AsyncSign(std::move(quorum), id, msgHash);
     762             : 
     763       18419 :     return true;
     764       49103 : }
     765             : 
     766      216234 : void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToRequest)
     767             : {
     768      216234 :     AssertLockHeld(cs);
     769             : 
     770      216234 :     int64_t now = GetTime<std::chrono::seconds>().count();
     771      216234 :     const size_t maxRequestsForNode = 32;
     772             : 
     773             :     // avoid requesting from same nodes all the time
     774      216234 :     std::vector<NodeId> shuffledNodeIds;
     775      216234 :     shuffledNodeIds.reserve(nodeStates.size());
     776      493393 :     for (const auto& [nodeId, nodeState] : nodeStates) {
     777      277159 :         if (nodeState.sessions.empty()) {
     778      171415 :             continue;
     779             :         }
     780      105744 :         shuffledNodeIds.emplace_back(nodeId);
     781             :     }
     782      216234 :     Shuffle(shuffledNodeIds.begin(), shuffledNodeIds.end(), rnd);
     783             : 
     784      321978 :     for (const auto& nodeId : shuffledNodeIds) {
     785      105744 :         auto& nodeState = nodeStates[nodeId];
     786             : 
     787      105744 :         if (nodeState.banned) {
     788          47 :             continue;
     789             :         }
     790             : 
     791      107488 :         nodeState.requestedSigShares.EraseIf([&now, &nodeId](const SigShareKey& k, int64_t t) {
     792        1791 :             if (now - t >= SIG_SHARE_REQUEST_TIMEOUT) {
     793             :                 // timeout while waiting for this one, so retry it with another node
     794          25 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::CollectSigSharesToRequest -- timeout while waiting for %s-%d, node=%d\n",
     795             :                          k.first.ToString(), k.second, nodeId);
     796          25 :                 return true;
     797             :             }
     798        1766 :             return false;
     799        1791 :         });
     800             : 
     801      105697 :         decltype(sigSharesToRequest.begin()->second)* invMap = nullptr;
     802             : 
     803     5035985 :         for (auto& [signHash, session] : nodeState.sessions) {
     804      800939 :             if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
     805        2690 :                 continue;
     806             :             }
     807             : 
     808     1596498 :             if (sigman.HasRecoveredSigForSession(signHash)) {
     809      127499 :                 continue;
     810             :             }
     811             : 
     812     3303545 :             for (const auto i : util::irange(session.announced.inv.size())) {
     813     2632795 :                 if (!session.announced.inv[i]) {
     814     2620935 :                     continue;
     815             :                 }
     816       11860 :                 auto k = std::make_pair(signHash, (uint16_t) i);
     817       11860 :                 if (sigShares.Has(k)) {
     818             :                     // we already have it
     819        6179 :                     session.announced.inv[i] = false;
     820        6179 :                     continue;
     821             :                 }
     822        5681 :                 if (nodeState.requestedSigShares.Size() >= maxRequestsForNode) {
     823             :                     // too many pending requests for this node
     824           0 :                     break;
     825             :                 }
     826        5681 :                 if (auto *const p = sigSharesRequested.Get(k)) {
     827        2498 :                     if (now - p->second >= SIG_SHARE_REQUEST_TIMEOUT && nodeId != p->first) {
     828             :                         // other node timed out, re-request from this node
     829           2 :                         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- other node timeout while waiting for %s-%d, re-request from=%d, node=%d\n", __func__,
     830             :                                  k.first.ToString(), k.second, nodeId, p->first);
     831           2 :                     } else {
     832        2496 :                         continue;
     833             :                     }
     834           2 :                 }
     835             :                 // if we got this far we should do a request
     836             : 
     837             :                 // track when we initiated the request so that we can detect timeouts
     838        3185 :                 nodeState.requestedSigShares.Add(k, now);
     839             : 
     840             :                 // don't request it from other nodes until a timeout happens
     841        3185 :                 auto& r = sigSharesRequested.GetOrAdd(k);
     842        3185 :                 r.first = nodeId;
     843        3185 :                 r.second = now;
     844             : 
     845        3185 :                 if (invMap == nullptr) {
     846        1987 :                     invMap = &sigSharesToRequest[nodeId];
     847        1987 :                 }
     848        6370 :                 auto& inv = (*invMap)[signHash];
     849        3185 :                 if (inv.inv.empty()) {
     850        3146 :                     const auto& llmq_params_opt = Params().GetLLMQ(session.llmqType);
     851        3146 :                     assert(llmq_params_opt.has_value());
     852        3146 :                     inv.Init(llmq_params_opt->size);
     853        3146 :                 }
     854        3185 :                 inv.inv[k.second] = true;
     855             : 
     856             :                 // don't request it again from this node
     857        3185 :                 session.announced.inv[i] = false;
     858             :             }
     859             :         }
     860             :     }
     861      216234 : }
     862             : 
     863      216234 : void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>>& sigSharesToSend)
     864             : {
     865      216234 :     AssertLockHeld(cs);
     866             : 
     867      504859 :     for (auto& [nodeId, nodeState] : nodeStates) {
     868      277159 :         if (nodeState.banned) {
     869          47 :             continue;
     870             :         }
     871             : 
     872      277112 :         decltype(sigSharesToSend.begin()->second)* sigSharesToSend2 = nullptr;
     873             : 
     874     3748715 :         for (auto& [signHash, session] : nodeState.sessions) {
     875      800939 :             if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
     876        2690 :                 continue;
     877             :             }
     878             : 
     879     1596498 :             if (sigman.HasRecoveredSigForSession(signHash)) {
     880      127504 :                 continue;
     881             :             }
     882             : 
     883      670745 :             CBatchedSigShares batchedSigShares;
     884             : 
     885     3303522 :             for (const auto i : util::irange(session.requested.inv.size())) {
     886     2632777 :                 if (!session.requested.inv[i]) {
     887     2613821 :                     continue;
     888             :                 }
     889       18956 :                 session.requested.inv[i] = false;
     890             : 
     891       18956 :                 auto k = std::make_pair(signHash, (uint16_t)i);
     892       18956 :                 const CSigShare* sigShare = sigShares.Get(k);
     893       18956 :                 if (sigShare == nullptr) {
     894             :                     // he requested something we don't have
     895          10 :                     session.requested.inv[i] = false;
     896          10 :                     continue;
     897             :                 }
     898             : 
     899       18946 :                 batchedSigShares.sigShares.emplace_back((uint16_t)i, sigShare->sigShare);
     900             :             }
     901             : 
     902      670745 :             if (!batchedSigShares.sigShares.empty()) {
     903       18921 :                 if (sigSharesToSend2 == nullptr) {
     904             :                     // only create the map if we actually add a batched sig
     905       22932 :                     sigSharesToSend2 = &sigSharesToSend[nodeId];
     906       11466 :                 }
     907       37842 :                 sigSharesToSend2->try_emplace(signHash, std::move(batchedSigShares));
     908       18921 :             }
     909      670745 :         }
     910             :     }
     911      216234 : }
     912             : 
     913      216234 : void CSigSharesManager::CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes)
     914             : {
     915      216234 :     AssertLockHeld(cs);
     916             : 
     917      216234 :     Uint256HashMap<CNode*> proTxToNode;
     918      960566 :     for (const auto& pnode : vNodes) {
     919      744332 :         auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
     920      744332 :         if (verifiedProRegTxHash.IsNull()) {
     921      200801 :             continue;
     922             :         }
     923      543531 :         proTxToNode.try_emplace(verifiedProRegTxHash, pnode);
     924             :     }
     925             : 
     926      216234 :     auto curTime = GetTime<std::chrono::milliseconds>().count();
     927             : 
     928      457204 :     for (auto& [_, signedSession] : signedSessions) {
     929       83407 :         if (!IsAllMembersConnectedEnabled(signedSession.quorum->params.type, m_sporkman)) {
     930           0 :             continue;
     931             :         }
     932             : 
     933      166814 :         if (signedSession.attempt >= signedSession.quorum->params.recoveryMembers) {
     934       43352 :             continue;
     935             :         }
     936             : 
     937       80110 :         if (curTime >= signedSession.nextAttemptTime) {
     938        8132 :             int64_t waitTime = exp2(signedSession.attempt) * EXP_SEND_FOR_RECOVERY_TIMEOUT;
     939        8132 :             waitTime = std::min(MAX_SEND_FOR_RECOVERY_TIMEOUT, waitTime);
     940       16264 :             signedSession.nextAttemptTime = curTime + waitTime;
     941       16264 :             auto dmn = SelectMemberForRecovery(*signedSession.quorum, signedSession.sigShare.getId(), signedSession.attempt);
     942        8132 :             signedSession.attempt++;
     943             : 
     944        8132 :             LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, sending to %s, attempt=%d\n", __func__,
     945             :                      signedSession.sigShare.GetSignHash().ToString(), dmn->proTxHash.ToString(), signedSession.attempt);
     946             : 
     947        8132 :             auto it = proTxToNode.find(dmn->proTxHash);
     948        8132 :             if (it == proTxToNode.end()) {
     949        1724 :                 continue;
     950             :             }
     951             : 
     952        6408 :             auto& m = sigSharesToSend[it->second->GetId()];
     953       12816 :             m.emplace_back(signedSession.sigShare);
     954        8132 :         }
     955             :     }
     956      216234 : }
     957             : 
     958      216234 : void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToAnnounce)
     959             : {
     960      216234 :     AssertLockHeld(cs);
     961             : 
     962      216234 :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, std::unordered_set<NodeId>, StaticSaltedHasher> quorumNodesMap;
     963             : 
     964             :     // TODO: remove NO_THREAD_SAFETY_ANALYSIS
     965             :     // using here template ForEach makes impossible to use lock annotation
     966      233478 :     sigSharesQueuedToAnnounce.ForEach([this, &quorumNodesMap, &sigSharesToAnnounce](const SigShareKey& sigShareKey,
     967             :                                                                                     bool) NO_THREAD_SAFETY_ANALYSIS {
     968       17244 :         AssertLockHeld(cs);
     969       17244 :         const auto& signHash = sigShareKey.first;
     970       17244 :         auto quorumMember = sigShareKey.second;
     971       17244 :         const CSigShare* sigShare = sigShares.Get(sigShareKey);
     972       17244 :         if (sigShare == nullptr) {
     973           0 :             return;
     974             :         }
     975             : 
     976             :         // announce to the nodes which we know through the intra-quorum-communication system
     977       17244 :         auto quorumKey = std::make_pair(sigShare->getLlmqType(), sigShare->getQuorumHash());
     978       17244 :         auto it = quorumNodesMap.find(quorumKey);
     979       17244 :         if (it == quorumNodesMap.end()) {
     980        8513 :             auto nodeIds = m_connman.GetMasternodeQuorumNodes(quorumKey.first, quorumKey.second);
     981        8513 :             it = quorumNodesMap.emplace(std::piecewise_construct, std::forward_as_tuple(quorumKey), std::forward_as_tuple(nodeIds.begin(), nodeIds.end())).first;
     982        8513 :         }
     983             : 
     984       17244 :         const auto& quorumNodes = it->second;
     985             : 
     986       49617 :         for (const auto& nodeId : quorumNodes) {
     987       32373 :             auto& nodeState = nodeStates[nodeId];
     988             : 
     989       32373 :             if (nodeState.banned) {
     990           0 :                 continue;
     991             :             }
     992             : 
     993       32373 :             auto& session = nodeState.GetOrCreateSessionFromShare(*sigShare);
     994             : 
     995       32373 :             if (session.knows.inv[quorumMember]) {
     996             :                 // he already knows that one
     997       17747 :                 continue;
     998             :             }
     999             : 
    1000       14626 :             auto& inv = sigSharesToAnnounce[nodeId][signHash];
    1001       14626 :             if (inv.inv.empty()) {
    1002       12203 :                 const auto& llmq_params_opt = Params().GetLLMQ(sigShare->getLlmqType());
    1003       12203 :                 assert(llmq_params_opt.has_value());
    1004       12203 :                 inv.Init(llmq_params_opt->size);
    1005       12203 :             }
    1006       14626 :             inv.inv[quorumMember] = true;
    1007       14626 :             session.knows.inv[quorumMember] = true;
    1008             :         }
    1009       17244 :     });
    1010             : 
    1011             :     // don't announce these anymore
    1012      216234 :     sigSharesQueuedToAnnounce.Clear();
    1013      216234 : }
    1014             : 
    1015      216234 : bool CSigSharesManager::SendMessages()
    1016             : {
    1017      216234 :     std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToRequest;
    1018      216234 :     std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>> sigShareBatchesToSend;
    1019      216234 :     std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesToSend;
    1020      216234 :     std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToAnnounce;
    1021      216234 :     std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
    1022             : 
    1023      250504 :     auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
    1024       34270 :         AssertLockHeld(cs);
    1025       34270 :         auto& nodeState = nodeStates[nodeId];
    1026       34270 :         auto* session = nodeState.GetSessionBySignHash(signHash);
    1027       34270 :         assert(session);
    1028       56106 :         while (session->sendSessionId == UNINITIALIZED_SESSION_ID) {
    1029       21836 :             const uint32_t session_id{GetRand<uint32_t>()};
    1030       43672 :             if (std::ranges::all_of(nodeState.sessions,
    1031      257326 :                                     [&session_id](const auto& s) { return s.second.sendSessionId != session_id; })) {
    1032             :                 // No session is using this id yet
    1033       21836 :                 session->sendSessionId = session_id;
    1034       43672 :                 sigSessionAnnouncements[nodeId].emplace_back(
    1035       43672 :                     CSigSesAnn(/*sessionId=*/session->sendSessionId, /*llmqType=*/session->llmqType,
    1036       21836 :                                /*quorumHash=*/session->quorumHash, /*id=*/session->id, /*msgHash=*/session->msgHash));
    1037       21836 :             }
    1038             :             // It's very unlikely that there is a session with the same id,
    1039             :             // but if there is one we just start over and pick another id
    1040             :         }
    1041       34270 :         return session->sendSessionId;
    1042             :     };
    1043             : 
    1044      216234 :     const CConnman::NodesSnapshot snap{m_connman, /* cond = */ CConnman::FullyConnectedOnly};
    1045             :     {
    1046      216234 :         LOCK(cs);
    1047      216234 :         CollectSigSharesToRequest(sigSharesToRequest);
    1048      216234 :         CollectSigSharesToSend(sigShareBatchesToSend);
    1049      216234 :         CollectSigSharesToAnnounce(sigSharesToAnnounce);
    1050      216234 :         CollectSigSharesToSendConcentrated(sigSharesToSend, snap.Nodes());
    1051             : 
    1052      221367 :         for (auto& [nodeId, sigShareMap] : sigSharesToRequest) {
    1053        5133 :             for (auto& [hash, sigShareInv] : sigShareMap) {
    1054        6292 :                 sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
    1055             :             }
    1056             :         }
    1057      246621 :         for (auto& [nodeId, sigShareBatchesMap] : sigShareBatchesToSend) {
    1058       30387 :             for (auto& [hash, sigShareBatch] : sigShareBatchesMap) {
    1059       37842 :                 sigShareBatch.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
    1060             :             }
    1061             :         }
    1062      235718 :         for (auto& [nodeId, sigShareMap] : sigSharesToAnnounce) {
    1063       19484 :             for (auto& [hash, sigShareInv] : sigShareMap) {
    1064       24406 :                 sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
    1065             :             }
    1066             :         }
    1067      216234 :     }
    1068             : 
    1069      216234 :     bool didSend = false;
    1070             : 
    1071      960566 :     for (auto& pnode : snap.Nodes()) {
    1072      744332 :         CNetMsgMaker msgMaker(pnode->GetCommonVersion());
    1073             : 
    1074      744332 :         if (const auto it1 = sigSessionAnnouncements.find(pnode->GetId()); it1 != sigSessionAnnouncements.end()) {
    1075       12997 :             std::vector<CSigSesAnn> msgs;
    1076       12997 :             msgs.reserve(it1->second.size());
    1077       34828 :             for (auto& sigSesAnn : it1->second) {
    1078       21831 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSESANN signHash=%s, sessionId=%d, node=%d\n",
    1079             :                          sigSesAnn.buildSignHash().ToString(), sigSesAnn.getSessionId(), pnode->GetId());
    1080       21831 :                 msgs.emplace_back(sigSesAnn);
    1081       21831 :                 if (msgs.size() == MAX_MSGS_CNT_QSIGSESANN) {
    1082           0 :                     m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
    1083           0 :                     msgs.clear();
    1084           0 :                     didSend = true;
    1085           0 :                 }
    1086             :             }
    1087       12997 :             if (!msgs.empty()) {
    1088       12997 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
    1089       12997 :                 didSend = true;
    1090       12997 :             }
    1091       12997 :         }
    1092             : 
    1093      744332 :         if (const auto it = sigSharesToRequest.find(pnode->GetId()); it != sigSharesToRequest.end()) {
    1094        1986 :             std::vector<CSigSharesInv> msgs;
    1095       11415 :             for (const auto& [signHash, inv] : it->second) {
    1096        3143 :                 assert(inv.CountSet() != 0);
    1097        3143 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QGETSIGSHARES signHash=%s, inv={%s}, node=%d\n",
    1098             :                          signHash.ToString(), inv.ToString(), pnode->GetId());
    1099        3143 :                 msgs.emplace_back(inv);
    1100        3143 :                 if (msgs.size() == MAX_MSGS_CNT_QSIGSHARES) {
    1101           0 :                     m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
    1102           0 :                     msgs.clear();
    1103           0 :                     didSend = true;
    1104           0 :                 }
    1105             :             }
    1106        1986 :             if (!msgs.empty()) {
    1107        1986 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
    1108        1986 :                 didSend = true;
    1109        1986 :             }
    1110        1986 :         }
    1111             : 
    1112      744332 :         if (const auto jt = sigShareBatchesToSend.find(pnode->GetId()); jt != sigShareBatchesToSend.end()) {
    1113       11463 :             size_t totalSigsCount = 0;
    1114       11463 :             std::vector<CBatchedSigShares> msgs;
    1115       49295 :             for (const auto& [signHash, inv] : jt->second) {
    1116       18916 :                 assert(!inv.sigShares.empty());
    1117       18916 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QBSIGSHARES signHash=%s, inv={%s}, node=%d\n",
    1118             :                          signHash.ToString(), inv.ToInvString(), pnode->GetId());
    1119       37832 :                 if (totalSigsCount + inv.sigShares.size() > MAX_MSGS_TOTAL_BATCHED_SIGS) {
    1120           0 :                     m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, msgs));
    1121           0 :                     msgs.clear();
    1122           0 :                     totalSigsCount = 0;
    1123           0 :                     didSend = true;
    1124           0 :                 }
    1125       18916 :                 totalSigsCount += inv.sigShares.size();
    1126       18916 :                 msgs.emplace_back(inv);
    1127             :             }
    1128       11463 :             if (!msgs.empty()) {
    1129       11463 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, std::move(msgs)));
    1130       11463 :                 didSend = true;
    1131       11463 :             }
    1132       11463 :         }
    1133             : 
    1134      744332 :         if (const auto kt = sigSharesToAnnounce.find(pnode->GetId()); kt != sigSharesToAnnounce.end()) {
    1135        7281 :             std::vector<CSigSharesInv> msgs;
    1136       43890 :             for (const auto& [signHash, inv] : kt->second) {
    1137       12203 :                 assert(inv.CountSet() != 0);
    1138       12203 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARESINV signHash=%s, inv={%s}, node=%d\n",
    1139             :                          signHash.ToString(), inv.ToString(), pnode->GetId());
    1140       12203 :                 msgs.emplace_back(inv);
    1141       12203 :                 if (msgs.size() == MAX_MSGS_CNT_QSIGSHARES) {
    1142           0 :                     m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
    1143           0 :                     msgs.clear();
    1144           0 :                     didSend = true;
    1145           0 :                 }
    1146             :             }
    1147        7281 :             if (!msgs.empty()) {
    1148        7281 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
    1149        7281 :                 didSend = true;
    1150        7281 :             }
    1151        7281 :         }
    1152             : 
    1153      744332 :         auto lt = sigSharesToSend.find(pnode->GetId());
    1154      744332 :         if (lt != sigSharesToSend.end()) {
    1155        5624 :             std::vector<CSigShare> msgs;
    1156       12032 :             for (auto& sigShare : lt->second) {
    1157        6408 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARE signHash=%s, node=%d\n",
    1158             :                          sigShare.GetSignHash().ToString(), pnode->GetId());
    1159        6408 :                 msgs.emplace_back(std::move(sigShare));
    1160        6408 :                 if (msgs.size() == MAX_MSGS_SIG_SHARES) {
    1161           0 :                     m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
    1162           0 :                     msgs.clear();
    1163           0 :                     didSend = true;
    1164           0 :                 }
    1165             :             }
    1166        5624 :             if (!msgs.empty()) {
    1167        5624 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
    1168        5624 :                 didSend = true;
    1169        5624 :             }
    1170        5624 :         }
    1171             :     }
    1172             : 
    1173      216234 :     return didSend;
    1174      216234 : }
    1175             : 
    1176       34226 : bool CSigSharesManager::GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo)
    1177             : {
    1178       34226 :     LOCK(cs);
    1179       34226 :     return nodeStates[nodeId].GetSessionInfoByRecvId(sessionId, retInfo);
    1180       34226 : }
    1181             : 
    1182       18652 : CSigShare CSigSharesManager::RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const std::pair<uint16_t, CBLSLazySignature>& in)
    1183             : {
    1184       55956 :     const auto& [member, sig] = in;
    1185       55956 :     CSigShare sigShare(session.llmqType, session.quorumHash, session.id, session.msgHash, member, sig);
    1186       18652 :     sigShare.UpdateKey();
    1187       18652 :     return sigShare;
    1188       18652 : }
    1189             : 
    1190      216234 : void CSigSharesManager::Cleanup()
    1191             : {
    1192      216234 :     constexpr auto CLEANUP_INTERVAL{5s};
    1193      216234 :     if (!cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) {
    1194      206298 :         return;
    1195             :     }
    1196             : 
    1197             :     // This map is first filled with all quorums found in all sig shares. Then we remove all inactive quorums and
    1198             :     // loop through all sig shares again to find the ones belonging to the inactive quorums. We then delete the
    1199             :     // sessions belonging to the sig shares. At the same time, we use this map as a cache when we later need to resolve
    1200             :     // quorumHash -> quorumPtr (as GetQuorum() requires cs_main, leading to deadlocks with cs held)
    1201        9936 :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
    1202             : 
    1203             :     {
    1204        9936 :         LOCK(cs);
    1205       35073 :         sigShares.ForEach([&quorums](const SigShareKey&, const CSigShare& sigShare) {
    1206       25137 :             quorums.try_emplace(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash()), nullptr);
    1207       25137 :         });
    1208        9936 :     }
    1209             : 
    1210             :     // Find quorums which became inactive
    1211       13525 :     for (auto it = quorums.begin(); it != quorums.end(); ) {
    1212        3589 :         if (IsQuorumActive(it->first.first, qman, it->first.second)) {
    1213        3376 :             auto quorum = qman.GetQuorum(it->first.first, it->first.second);
    1214        3376 :             if (quorum) {
    1215        3376 :                 it->second = quorum;
    1216        3376 :                 ++it;
    1217        3376 :                 continue;
    1218             :             }
    1219        3376 :         }
    1220         213 :         it = quorums.erase(it);
    1221             :     }
    1222             : 
    1223             :     {
    1224             :         // Now delete sessions which are for inactive quorums
    1225        9936 :         LOCK(cs);
    1226        9936 :         Uint256HashSet inactiveQuorumSessions;
    1227       35139 :         sigShares.ForEach([&quorums, &inactiveQuorumSessions](const SigShareKey&, const CSigShare& sigShare) {
    1228       25203 :             if (quorums.count(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash())) == 0) {
    1229        1913 :                 inactiveQuorumSessions.emplace(sigShare.GetSignHash());
    1230        1913 :             }
    1231       25203 :         });
    1232       11130 :         for (const auto& signHash : inactiveQuorumSessions) {
    1233        1194 :             RemoveSigSharesForSession(signHash);
    1234             :         }
    1235        9936 :     }
    1236             : 
    1237             :     {
    1238        9936 :         LOCK(cs);
    1239             : 
    1240             :         // Remove sessions which were successfully recovered
    1241        9936 :         Uint256HashSet doneSessions;
    1242       33226 :         sigShares.ForEach([&doneSessions, this](const SigShareKey&, const CSigShare& sigShare) {
    1243       23290 :             if (doneSessions.count(sigShare.GetSignHash()) != 0) {
    1244           6 :                 return;
    1245             :             }
    1246       23284 :             if (sigman.HasRecoveredSigForSession(sigShare.GetSignHash())) {
    1247          47 :                 doneSessions.emplace(sigShare.GetSignHash());
    1248          47 :             }
    1249       23290 :         });
    1250        9983 :         for (const auto& signHash : doneSessions) {
    1251          47 :             RemoveSigSharesForSession(signHash);
    1252             :         }
    1253             : 
    1254             :         // Remove sessions which timed out
    1255        9936 :         Uint256HashSet timeoutSessions;
    1256        9936 :         int64_t now = GetTime<std::chrono::seconds>().count();
    1257       24449 :         for (const auto& [signHash, lastSeenTime] : timeSeenForSessions) {
    1258       29026 :             if (now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
    1259        1803 :                 timeoutSessions.emplace(signHash);
    1260        1803 :             }
    1261             :         }
    1262       11739 :         for (const auto& signHash : timeoutSessions) {
    1263             : 
    1264        1803 :             if (const size_t count = sigShares.CountForSignHash(signHash); count > 0) {
    1265        1803 :                 const auto* m = sigShares.GetAllForSignHash(signHash);
    1266        1803 :                 assert(m);
    1267             : 
    1268        1803 :                 const auto& oneSigShare = m->begin()->second;
    1269             : 
    1270        1803 :                 std::string strMissingMembers;
    1271        1803 :                 if (LogAcceptDebug(BCLog::LLMQ_SIGS)) {
    1272        1803 :                     if (const auto quorumIt = quorums.find(std::make_pair(oneSigShare.getLlmqType(), oneSigShare.getQuorumHash())); quorumIt != quorums.end()) {
    1273        1803 :                         const auto& quorum = quorumIt->second;
    1274        9333 :                         for (const auto i : util::irange(quorum->members.size())) {
    1275        7530 :                             if (m->count((uint16_t)i) == 0) {
    1276        4830 :                                 const auto& dmn = quorum->members[i];
    1277        4830 :                                 strMissingMembers += strprintf("\n  %s", dmn->proTxHash.ToString());
    1278        4830 :                             }
    1279             :                         }
    1280        1803 :                     }
    1281        1803 :                 }
    1282             : 
    1283        1803 :                 LogPrintLevel(BCLog::LLMQ_SIGS, BCLog::Level::Info, /* Continued */
    1284             :                               "CSigSharesManager::%s -- signing session timed out. signHash=%s, id=%s, msgHash=%s, "
    1285             :                               "sigShareCount=%d, missingMembers=%s\n",
    1286             :                               __func__, signHash.ToString(), oneSigShare.getId().ToString(),
    1287             :                               oneSigShare.getMsgHash().ToString(), count, strMissingMembers);
    1288        1803 :             } else {
    1289           0 :                 LogPrintLevel(BCLog::LLMQ_SIGS, BCLog::Level::Info, /* Continued */
    1290             :                               "CSigSharesManager::%s -- signing session timed out. signHash=%s, sigShareCount=%d\n",
    1291             :                               __func__, signHash.ToString(), count);
    1292             :             }
    1293        1803 :             RemoveSigSharesForSession(signHash);
    1294             :         }
    1295        9936 :     }
    1296             : 
    1297             :     // Find node states for peers that disappeared from CConnman
    1298        9936 :     std::unordered_set<NodeId> nodeStatesToDelete;
    1299             :     {
    1300        9936 :         LOCK(cs);
    1301       21585 :         for (const auto& [nodeId, _] : nodeStates) {
    1302       11649 :             nodeStatesToDelete.emplace(nodeId);
    1303             :         }
    1304        9936 :     }
    1305       39912 :     m_connman.ForEachNode([&nodeStatesToDelete](const CNode* pnode) { nodeStatesToDelete.erase(pnode->GetId()); });
    1306             : 
    1307             :     // Now delete these node states
    1308        9936 :     LOCK(cs);
    1309       10392 :     for (const auto& nodeId : nodeStatesToDelete) {
    1310         456 :         auto it = nodeStates.find(nodeId);
    1311         456 :         if (it == nodeStates.end()) {
    1312           0 :             continue;
    1313             :         }
    1314             :         // remove global requested state to force a re-request from another node
    1315             :         // TODO: remove NO_THREAD_SAFETY_ANALYSIS
    1316             :         // using here template ForEach makes impossible to use lock annotation
    1317         456 :         it->second.requestedSigShares.ForEach([this](const SigShareKey& k, bool) NO_THREAD_SAFETY_ANALYSIS {
    1318           0 :             AssertLockHeld(cs);
    1319           0 :             sigSharesRequested.Erase(k);
    1320           0 :         });
    1321         456 :         nodeStates.erase(nodeId);
    1322             :     }
    1323      216234 : }
    1324             : 
    1325       30006 : void CSigSharesManager::RemoveSigSharesForSession(const uint256& signHash)
    1326             : {
    1327       30006 :     AssertLockHeld(cs);
    1328             : 
    1329      118730 :     for (auto& [_, nodeState] : nodeStates) {
    1330       88724 :         nodeState.RemoveSession(signHash);
    1331             :     }
    1332             : 
    1333       30006 :     sigSharesRequested.EraseAllForSignHash(signHash);
    1334       30006 :     sigSharesQueuedToAnnounce.EraseAllForSignHash(signHash);
    1335       30006 :     sigShares.EraseAllForSignHash(signHash);
    1336       30006 :     signedSessions.erase(signHash);
    1337       30006 :     timeSeenForSessions.erase(signHash);
    1338       30006 : }
    1339             : 
    1340      216234 : void CSigSharesManager::RemoveNodesIf(std::function<bool(NodeId)> predicate)
    1341             : {
    1342      216234 :     LOCK(cs);
    1343      493395 :     for (auto it = nodeStates.begin(); it != nodeStates.end();) {
    1344      277161 :         if (predicate(it->first)) {
    1345             :             // re-request sigshares from other nodes
    1346             :             // TODO: remove NO_THREAD_SAFETY_ANALYSIS
    1347             :             // using here template ForEach makes impossible to use lock annotation
    1348           2 :             it->second.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) NO_THREAD_SAFETY_ANALYSIS {
    1349           0 :                 AssertLockHeld(cs);
    1350           0 :                 sigSharesRequested.Erase(k);
    1351           0 :             });
    1352           2 :             it = nodeStates.erase(it);
    1353           2 :         } else {
    1354      277159 :             ++it;
    1355             :         }
    1356             :     }
    1357      216234 : }
    1358             : 
    1359           9 : void CSigSharesManager::MarkAsBanned(NodeId nodeId)
    1360             : {
    1361           9 :     if (nodeId == -1) {
    1362           0 :         return;
    1363             :     }
    1364             : 
    1365           9 :     LOCK(cs);
    1366           9 :     auto it = nodeStates.find(nodeId);
    1367           9 :     if (it == nodeStates.end()) {
    1368           0 :         return;
    1369             :     }
    1370             : 
    1371           9 :     auto& nodeState = it->second;
    1372             :     // Whatever we requested from him, let's request it from someone else now
    1373             :     // TODO: remove NO_THREAD_SAFETY_ANALYSIS
    1374             :     // using here template ForEach makes impossible to use lock annotation
    1375           9 :     nodeState.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) NO_THREAD_SAFETY_ANALYSIS {
    1376           0 :         AssertLockHeld(cs);
    1377           0 :         sigSharesRequested.Erase(k);
    1378           0 :     });
    1379           9 :     nodeState.requestedSigShares.Clear();
    1380           9 :     nodeState.banned = true;
    1381           9 : }
    1382             : 
    1383      708911 : std::vector<PendingSignatureData> CSigSharesManager::DispatchPendingSigns()
    1384             : {
    1385             :     // Swap out entire vector to avoid lock thrashing
    1386      708911 :     std::vector<PendingSignatureData> signs;
    1387             : 
    1388      708911 :     LOCK(cs_pendingSigns);
    1389      708911 :     signs.swap(pendingSigns);
    1390             : 
    1391      708911 :     return signs;
    1392      708911 : }
    1393             : 
    1394           0 : bool CSigSharesManager::IsAnyPendingProcessing() const
    1395             : {
    1396           0 :     LOCK(cs);
    1397             :     // Check if there's work, spawn a helper if so
    1398           0 :     return std::any_of(nodeStates.begin(), nodeStates.end(),
    1399           0 :                        [](const auto& entry) { return !entry.second.pendingIncomingSigShares.Empty(); });
    1400           0 : }
    1401             : 
    1402       18412 : std::shared_ptr<CRecoveredSig> CSigSharesManager::SignAndProcessSingleShare(PendingSignatureData work)
    1403             : {
    1404       18412 :     auto opt_sigShare = CreateSigShare(*work.quorum, work.id, work.msgHash);
    1405             : 
    1406       18412 :     if (opt_sigShare.has_value() && opt_sigShare->sigShare.Get().IsValid()) {
    1407       18358 :         auto& sigShare = *opt_sigShare;
    1408       18358 :         auto rs = ProcessSigShare(sigShare, work.quorum);
    1409             : 
    1410       18358 :         if (IsAllMembersConnectedEnabled(work.quorum->params.type, m_sporkman)) {
    1411        7083 :             LOCK(cs);
    1412        7083 :             auto& session = signedSessions[sigShare.GetSignHash()];
    1413        7083 :             session.sigShare = std::move(sigShare);
    1414        7083 :             session.quorum = work.quorum;
    1415        7083 :             session.nextAttemptTime = 0;
    1416        7083 :             session.attempt = 0;
    1417        7083 :         }
    1418       18358 :         return rs;
    1419       18358 :     }
    1420          54 :     return nullptr;
    1421       18414 : }
    1422             : 
    1423       18419 : void CSigSharesManager::AsyncSign(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash)
    1424             : {
    1425       18419 :     LOCK(cs_pendingSigns);
    1426       18419 :     pendingSigns.emplace_back(std::move(quorum), id, msgHash);
    1427       18419 : }
    1428             : 
    1429         249 : std::optional<CSigShare> CSigSharesManager::CreateSigShareForSingleMember(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
    1430             : {
    1431         249 :     cxxtimer::Timer t(true);
    1432         249 :     auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
    1433             : 
    1434         249 :     int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
    1435         249 :     if (memberIdx == -1) {
    1436             :         // this should really not happen (IsValidMember gave true)
    1437           0 :         return std::nullopt;
    1438             :     }
    1439             : 
    1440         249 :     CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
    1441         249 :     uint256 signHash = sigShare.buildSignHash().Get();
    1442             : 
    1443             :     // TODO: This one should be SIGN by QUORUM key, not by OPERATOR key
    1444             :     // see TODO in CDKGSession::FinalizeSingleCommitment for details
    1445         241 :     auto bls_scheme = bls::bls_legacy_scheme.load();
    1446         241 :     sigShare.sigShare.Set(m_mn_activeman.Sign(signHash, bls_scheme), bls_scheme);
    1447             : 
    1448         243 :     if (!sigShare.sigShare.Get().IsValid()) {
    1449           0 :         LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n",
    1450             :                   __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(),
    1451             :                   t.count());
    1452           0 :         return std::nullopt;
    1453             :     }
    1454             : 
    1455         242 :     sigShare.UpdateKey();
    1456             : 
    1457         242 :     LogPrint(BCLog::LLMQ_SIGS, /* Continued */
    1458             :              "CSigSharesManager::%s -- created sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, "
    1459             :              "time=%s\n",
    1460             :              __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(),
    1461             :              std23::to_underlying(quorum.params.type), quorum.qc->quorumHash.ToString(), t.count());
    1462             : 
    1463         243 :     return sigShare;
    1464         255 : }
    1465             : 
    1466       18556 : std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
    1467             : {
    1468       18556 :     auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
    1469             : 
    1470       18556 :     if (!quorum.IsValidMember(activeMasterNodeProTxHash)) {
    1471           2 :         return std::nullopt;
    1472             :     }
    1473             : 
    1474       18554 :     if (quorum.params.is_single_member()) {
    1475         241 :         return CreateSigShareForSingleMember(quorum, id, msgHash);
    1476             :     }
    1477       18313 :     cxxtimer::Timer t(true);
    1478       18313 :     const CBLSSecretKey& skShare = quorum.GetSkShare();
    1479       18313 :     if (!skShare.IsValid()) {
    1480          54 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have our skShare for quorum %s\n", __func__, quorum.qc->quorumHash.ToString());
    1481          54 :         return std::nullopt;
    1482             :     }
    1483             : 
    1484       18074 :     int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
    1485       18106 :     if (memberIdx == -1) {
    1486             :         // this should really not happen (IsValidMember gave true)
    1487           0 :         return std::nullopt;
    1488             :     }
    1489             : 
    1490       18106 :     CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
    1491       18259 :     uint256 signHash = sigShare.buildSignHash().Get();
    1492             : 
    1493       18063 :     auto bls_scheme = bls::bls_legacy_scheme.load();
    1494       18063 :     sigShare.sigShare.Set(skShare.Sign(signHash, bls_scheme), bls_scheme);
    1495       18117 :     if (!sigShare.sigShare.Get().IsValid()) {
    1496           0 :         LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__,
    1497             :                   signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), t.count());
    1498           0 :         return std::nullopt;
    1499             :     }
    1500             : 
    1501       18094 :     sigShare.UpdateKey();
    1502             : 
    1503       18115 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- created sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, time=%s\n", __func__,
    1504             :               signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), std23::to_underlying(quorum.params.type), quorum.qc->quorumHash.ToString(), t.count());
    1505             : 
    1506       18121 :     return sigShare;
    1507       19114 : }
    1508             : 
    1509             : // causes all known sigShares to be re-announced
    1510         887 : void CSigSharesManager::ForceReAnnouncement(const CQuorum& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
    1511             : {
    1512         887 :     if (IsAllMembersConnectedEnabled(llmqType, m_sporkman)) {
    1513           9 :         return;
    1514             :     }
    1515             : 
    1516         878 :     LOCK(cs);
    1517         878 :     auto signHash = SignHash(llmqType, quorum.qc->quorumHash, id, msgHash).Get();
    1518         878 :     if (const auto *const sigs = sigShares.GetAllForSignHash(signHash)) {
    1519          96 :         for (const auto& [quorumMemberIndex, _] : *sigs) {
    1520             :             // re-announce every sigshare to every node
    1521         114 :             sigSharesQueuedToAnnounce.Add(std::make_pair(signHash, quorumMemberIndex), true);
    1522             :         }
    1523          39 :     }
    1524        2879 :     for (auto& [_, nodeState] : nodeStates) {
    1525        2001 :         auto* session = nodeState.GetSessionBySignHash(signHash);
    1526        2001 :         if (session == nullptr) {
    1527        1904 :             continue;
    1528             :         }
    1529             :         // pretend that the other node doesn't know about any shares so that we re-announce everything
    1530          97 :         session->knows.SetAll(false);
    1531             :         // we need to use a new session id as we don't know if the other node has run into a timeout already
    1532          97 :         session->sendSessionId = UNINITIALIZED_SESSION_ID;
    1533             :     }
    1534         887 : }
    1535             : 
    1536       26962 : RecoveredSigResult CSigSharesManager::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
    1537             : {
    1538       26962 :     auto signHash = recoveredSig.buildSignHash().Get();
    1539       26962 :     LOCK(cs);
    1540       26962 :     RemoveSigSharesForSession(signHash);
    1541       26962 :     return std::monostate{};
    1542       26962 : }
    1543             : } // namespace llmq

Generated by: LCOV version 1.16