LCOV - code coverage report
Current view: top level - src/llmq - signing_shares.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 0 945 0.0 %
Date: 2026-06-25 07:23:51 Functions: 0 71 0.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           0 : void CSigShare::UpdateKey()
      30             : {
      31           0 :     key.first = this->buildSignHash().Get();
      32           0 :     key.second = quorumMember;
      33           0 : }
      34             : 
      35           0 : std::string CSigSesAnn::ToString() const
      36             : {
      37           0 :     return strprintf("sessionId=%d, llmqType=%d, quorumHash=%s, id=%s, msgHash=%s",
      38           0 :                      sessionId, std23::to_underlying(getLlmqType()), getQuorumHash().ToString(), getId().ToString(), getMsgHash().ToString());
      39           0 : }
      40             : 
      41           0 : void CSigSharesInv::Merge(const CSigSharesInv& inv2)
      42             : {
      43           0 :     for (const auto i : util::irange(inv.size())) {
      44           0 :         if (inv2.inv[i]) {
      45           0 :             inv[i] = inv2.inv[i];
      46           0 :         }
      47             :     }
      48           0 : }
      49             : 
      50           0 : size_t CSigSharesInv::CountSet() const
      51             : {
      52           0 :     return (size_t)std::count(inv.begin(), inv.end(), true);
      53             : }
      54             : 
      55           0 : std::string CSigSharesInv::ToString() const
      56             : {
      57           0 :     std::string str = "(";
      58           0 :     bool first = true;
      59           0 :     for (const auto i : util::irange(inv.size())) {
      60           0 :         if (!inv[i]) {
      61           0 :             continue;
      62             :         }
      63             : 
      64           0 :         if (!first) {
      65           0 :             str += ",";
      66           0 :         }
      67           0 :         first = false;
      68           0 :         str += strprintf("%d", i);
      69             :     }
      70           0 :     str += ")";
      71           0 :     return str;
      72           0 : }
      73             : 
      74           0 : void CSigSharesInv::Init(size_t size)
      75             : {
      76           0 :     inv.resize(size, false);
      77           0 : }
      78             : 
      79           0 : void CSigSharesInv::Set(uint16_t quorumMember, bool v)
      80             : {
      81           0 :     assert(quorumMember < inv.size());
      82           0 :     inv[quorumMember] = v;
      83           0 : }
      84             : 
      85           0 : void CSigSharesInv::SetAll(bool v)
      86             : {
      87           0 :     std::fill(inv.begin(), inv.end(), v);
      88           0 : }
      89             : 
      90           0 : std::string CBatchedSigShares::ToInvString() const
      91             : {
      92           0 :     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           0 :     inv.Init(400);
      95           0 :     for (const auto& sigShare : sigShares) {
      96           0 :         inv.inv[sigShare.first] = true;
      97             :     }
      98           0 :     return inv.ToString();
      99           0 : }
     100             : 
     101           0 : static void InitSession(CSigSharesNodeState::Session& s, const llmq::SignHash& signHash, CSigBase from)
     102             : {
     103           0 :     const auto& llmq_params_opt = Params().GetLLMQ(from.getLlmqType());
     104           0 :     assert(llmq_params_opt.has_value());
     105           0 :     const auto& llmq_params = llmq_params_opt.value();
     106             : 
     107           0 :     s.llmqType = from.getLlmqType();
     108           0 :     s.quorumHash = from.getQuorumHash();
     109           0 :     s.id = from.getId();
     110           0 :     s.msgHash = from.getMsgHash();
     111           0 :     s.signHash = signHash;
     112           0 :     s.announced.Init((size_t)llmq_params.size);
     113           0 :     s.requested.Init((size_t)llmq_params.size);
     114           0 :     s.knows.Init((size_t)llmq_params.size);
     115           0 : }
     116             : 
     117           0 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromShare(const llmq::CSigShare& sigShare)
     118             : {
     119           0 :     auto& s = sessions[sigShare.GetSignHash()];
     120           0 :     if (s.announced.inv.empty()) {
     121           0 :         InitSession(s, sigShare.buildSignHash(), sigShare);
     122           0 :     }
     123           0 :     return s;
     124             : }
     125             : 
     126           0 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromAnn(const llmq::CSigSesAnn& ann)
     127             : {
     128           0 :     auto signHash = ann.buildSignHash();
     129           0 :     auto& s = sessions[signHash.Get()];
     130           0 :     if (s.announced.inv.empty()) {
     131           0 :         InitSession(s, signHash, ann);
     132           0 :     }
     133           0 :     return s;
     134             : }
     135             : 
     136           0 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionBySignHash(const uint256& signHash)
     137             : {
     138           0 :     auto it = sessions.find(signHash);
     139           0 :     if (it == sessions.end()) {
     140           0 :         return nullptr;
     141             :     }
     142           0 :     return &it->second;
     143           0 : }
     144             : 
     145           0 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionByRecvId(uint32_t sessionId)
     146             : {
     147           0 :     auto it = sessionByRecvId.find(sessionId);
     148           0 :     if (it == sessionByRecvId.end()) {
     149           0 :         return nullptr;
     150             :     }
     151           0 :     return it->second;
     152           0 : }
     153             : 
     154           0 : bool CSigSharesNodeState::GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo)
     155             : {
     156           0 :     const auto* s = GetSessionByRecvId(sessionId);
     157           0 :     if (s == nullptr) {
     158           0 :         return false;
     159             :     }
     160           0 :     retInfo.llmqType = s->llmqType;
     161           0 :     retInfo.quorumHash = s->quorumHash;
     162           0 :     retInfo.id = s->id;
     163           0 :     retInfo.msgHash = s->msgHash;
     164           0 :     retInfo.signHash = s->signHash;
     165           0 :     retInfo.quorum = s->quorum;
     166             : 
     167           0 :     return true;
     168           0 : }
     169             : 
     170           0 : void CSigSharesNodeState::RemoveSession(const uint256& signHash)
     171             : {
     172           0 :     if (const auto it = sessions.find(signHash); it != sessions.end()) {
     173           0 :         sessionByRecvId.erase(it->second.recvSessionId);
     174           0 :         sessions.erase(it);
     175           0 :     }
     176           0 :     requestedSigShares.EraseAllForSignHash(signHash);
     177           0 :     pendingIncomingSigShares.EraseAllForSignHash(signHash);
     178           0 : }
     179             : 
     180             : //////////////////////
     181             : 
     182           0 : CSigSharesManager::CSigSharesManager(CConnman& connman, const ChainstateManager& chainman, CSigningManager& _sigman,
     183             :                                      const CActiveMasternodeManager& mn_activeman, const CQuorumManager& _qman,
     184             :                                      const CSporkManager& sporkman) :
     185           0 :     m_connman{connman},
     186           0 :     m_chainman{chainman},
     187           0 :     sigman{_sigman},
     188           0 :     m_mn_activeman{mn_activeman},
     189           0 :     qman{_qman},
     190           0 :     m_sporkman{sporkman}
     191           0 : {
     192           0 : }
     193             : 
     194           0 : CSigSharesManager::~CSigSharesManager() = default;
     195             : 
     196           0 : void CSigSharesManager::RegisterRecoveryInterface()
     197             : {
     198           0 :     sigman.RegisterRecoveredSigsListener(this);
     199           0 : }
     200             : 
     201           0 : void CSigSharesManager::UnregisterRecoveryInterface()
     202             : {
     203           0 :     sigman.UnregisterRecoveredSigsListener(this);
     204           0 : }
     205             : 
     206           0 : bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann)
     207             : {
     208           0 :     auto llmqType = ann.getLlmqType();
     209           0 :     if (!Params().GetLLMQ(llmqType).has_value()) {
     210           0 :         return false;
     211             :     }
     212           0 :     if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) {
     213           0 :         return false;
     214             :     }
     215             : 
     216           0 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- ann={%s}, node=%d\n", __func__, ann.ToString(), pfrom.GetId());
     217             : 
     218           0 :     auto quorum = qman.GetQuorum(llmqType, ann.getQuorumHash());
     219           0 :     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           0 :     LOCK(cs);
     227           0 :     auto& nodeState = nodeStates[pfrom.GetId()];
     228           0 :     auto& session = nodeState.GetOrCreateSessionFromAnn(ann);
     229           0 :     nodeState.sessionByRecvId.erase(session.recvSessionId);
     230           0 :     nodeState.sessionByRecvId.erase(ann.getSessionId());
     231           0 :     session.recvSessionId = ann.getSessionId();
     232           0 :     session.quorum = quorum;
     233           0 :     nodeState.sessionByRecvId.try_emplace(ann.getSessionId(), &session);
     234             : 
     235           0 :     return true;
     236           0 : }
     237             : 
     238           0 : static bool VerifySigSharesInv(Consensus::LLMQType llmqType, const CSigSharesInv& inv)
     239             : {
     240           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     241           0 :     return llmq_params_opt.has_value() && (inv.inv.size() == size_t(llmq_params_opt->size));
     242             : }
     243             : 
     244           0 : bool CSigSharesManager::ProcessMessageSigShares(const CNode& pfrom, const CSigSharesInv& inv, const std::string& msg_type)
     245             : {
     246           0 :     CSigSharesNodeState::SessionInfo sessionInfo;
     247           0 :     if (!GetSessionInfoByRecvId(pfrom.GetId(), inv.sessionId, sessionInfo)) {
     248           0 :         return true;
     249             :     }
     250             : 
     251           0 :     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           0 :     if (sigman.HasRecoveredSigForSession(sessionInfo.signHash.Get())) {
     257           0 :         return true;
     258             :     }
     259             : 
     260           0 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, inv={%s}, node=%d\n", __func__,
     261             :             sessionInfo.signHash.ToString(), inv.ToString(), pfrom.GetId());
     262             : 
     263           0 :     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           0 :     LOCK(cs);
     271           0 :     auto& nodeState = nodeStates[pfrom.GetId()];
     272           0 :     auto* session = nodeState.GetSessionByRecvId(inv.sessionId);
     273           0 :     if (session == nullptr) {
     274           0 :         return true;
     275             :     }
     276           0 :     if (msg_type == NetMsgType::QSIGSHARESINV) {
     277           0 :         session->announced.Merge(inv);
     278           0 :     } else { // msg_type == NetMsgType::QGETSIGSHARES
     279           0 :         session->requested.Merge(inv);
     280             :     }
     281             : 
     282           0 :     session->knows.Merge(inv);
     283           0 :     return true;
     284           0 : }
     285             : 
     286             : // Failure is not issue, we should not ban node
     287           0 : static bool PreVerifySigShareQuorum(const CActiveMasternodeManager& mn_activeman, const CQuorumManager& quorum_manager,
     288             :                                     const CQuorumCPtr& quorum, Consensus::LLMQType llmqType)
     289             : {
     290           0 :     if (!IsQuorumActive(llmqType, quorum_manager, quorum->qc->quorumHash)) {
     291             :         // quorum is too old
     292           0 :         return false;
     293             :     }
     294           0 :     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           0 :         return false;
     297             :     }
     298           0 :     if (!quorum->HasVerificationVector()) {
     299             :         // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
     300           0 :         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           0 :         return false;
     303             :     }
     304           0 :     return true;
     305           0 : }
     306             : 
     307             : // Ban node if PreVerifyBatchedSigShares failed
     308           0 : bool PreVerifyBatchedSigShares(const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares)
     309             : {
     310           0 :     std::unordered_set<uint16_t> dupMembers;
     311             : 
     312           0 :     for (const auto& [quorumMember, _] : batchedSigShares.sigShares) {
     313           0 :         if (!dupMembers.emplace(quorumMember).second) {
     314           0 :             return false;
     315             :         }
     316             : 
     317           0 :         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           0 :         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           0 :     return true;
     327           0 : }
     328             : 
     329           0 : bool CSigSharesManager::ProcessMessageBatchedSigShares(const CNode& pfrom, const CBatchedSigShares& batchedSigShares)
     330             : {
     331           0 :     CSigSharesNodeState::SessionInfo sessionInfo;
     332           0 :     if (!GetSessionInfoByRecvId(pfrom.GetId(), batchedSigShares.sessionId, sessionInfo)) {
     333           0 :         return true;
     334             :     }
     335             : 
     336           0 :     if (!PreVerifySigShareQuorum(m_mn_activeman, qman, sessionInfo.quorum, sessionInfo.llmqType)) {
     337           0 :         return true;
     338             :     }
     339             : 
     340           0 :     if (!PreVerifyBatchedSigShares(sessionInfo, batchedSigShares)) {
     341           0 :         return false; // ban node
     342             :     }
     343             : 
     344           0 :     std::vector<CSigShare> sigSharesToProcess;
     345           0 :     sigSharesToProcess.reserve(batchedSigShares.sigShares.size());
     346             : 
     347             :     {
     348           0 :         LOCK(cs);
     349           0 :         auto& nodeState = nodeStates[pfrom.GetId()];
     350             : 
     351           0 :         for (const auto& sigSharetmp : batchedSigShares.sigShares) {
     352           0 :             CSigShare sigShare = RebuildSigShare(sessionInfo, sigSharetmp);
     353           0 :             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           0 :             if (sigShares.Has(sigShare.GetKey())) {
     360           0 :                 continue;
     361             :             }
     362             : 
     363             :             // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
     364           0 :             if (sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId())) {
     365           0 :                 continue;
     366             :             }
     367             : 
     368           0 :             sigSharesToProcess.emplace_back(sigShare);
     369           0 :         }
     370           0 :     }
     371             : 
     372           0 :     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           0 :     if (sigSharesToProcess.empty()) {
     376           0 :         return true;
     377             :     }
     378             : 
     379           0 :     LOCK(cs);
     380           0 :     auto& nodeState = nodeStates[pfrom.GetId()];
     381           0 :     for (const auto& s : sigSharesToProcess) {
     382           0 :         nodeState.pendingIncomingSigShares.Add(s.GetKey(), s);
     383             :     }
     384           0 :     return true;
     385           0 : }
     386             : 
     387           0 : bool CSigSharesManager::ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare)
     388             : {
     389           0 :     auto quorum = qman.GetQuorum(sigShare.getLlmqType(), sigShare.getQuorumHash());
     390           0 :     if (!quorum) {
     391           0 :         return true;
     392             :     }
     393           0 :     if (!PreVerifySigShareQuorum(m_mn_activeman, qman, quorum, sigShare.getLlmqType())) {
     394           0 :         return true;
     395             :     }
     396             : 
     397           0 :     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           0 :     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           0 :     const auto signHash = sigShare.GetSignHash();
     407           0 :     const bool alreadyRecovered = sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId()) ||
     408           0 :                                   sigman.HasRecoveredSigForSession(signHash);
     409             : 
     410             :     {
     411           0 :         LOCK(cs);
     412             : 
     413           0 :         if (alreadyRecovered) {
     414           0 :             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           0 :             return true;
     420             :         }
     421             : 
     422           0 :         if (sigShares.Has(sigShare.GetKey())) {
     423           0 :             return true;
     424             :         }
     425             : 
     426           0 :         auto& nodeState = nodeStates[fromId];
     427           0 :         nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
     428           0 :     }
     429             : 
     430           0 :     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           0 :     return true;
     433           0 : }
     434             : 
     435           0 : 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           0 :     bool more_work{false};
     440             : 
     441             :     {
     442           0 :         LOCK(cs);
     443           0 :         if (nodeStates.empty()) {
     444           0 :             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           0 :         std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
     453           0 :         IterateNodesRandom(
     454           0 :             nodeStates,
     455           0 :             [&]() {
     456           0 :                 return uniqueSignHashes.size() < maxUniqueSessions;
     457             :                 // TODO: remove NO_THREAD_SAFETY_ANALYSIS
     458             :                 // using here template IterateNodesRandom makes impossible to use lock annotation
     459             :             },
     460           0 :             [&](NodeId nodeId, CSigSharesNodeState& ns) NO_THREAD_SAFETY_ANALYSIS {
     461           0 :                 if (ns.pendingIncomingSigShares.Empty()) {
     462           0 :                     return false;
     463             :                 }
     464           0 :                 const auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
     465             : 
     466           0 :                 AssertLockHeld(cs);
     467           0 :                 if (const bool alreadyHave = this->sigShares.Has(sigShare.GetKey()); !alreadyHave) {
     468           0 :                     uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
     469           0 :                     retSigShares[nodeId].emplace_back(sigShare);
     470           0 :                 }
     471           0 :                 ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
     472           0 :                 return !ns.pendingIncomingSigShares.Empty();
     473           0 :             },
     474           0 :             rnd);
     475             : 
     476           0 :         if (retSigShares.empty()) {
     477           0 :             return false;
     478             :         }
     479             : 
     480             :         // Determine if there is still work left in any node state after pulling this batch
     481           0 :         more_work = std::any_of(nodeStates.begin(), nodeStates.end(),
     482           0 :                                 [](const auto& entry) {
     483           0 :                                     const auto& ns = entry.second;
     484           0 :                                     return !ns.pendingIncomingSigShares.Empty();
     485             :                                 });
     486           0 :     }
     487             : 
     488             :     // For the convenience of the caller, also build a map of quorumHash -> quorum
     489             : 
     490           0 :     for (const auto& [_, vecSigShares] : retSigShares) {
     491           0 :         for (const auto& sigShare : vecSigShares) {
     492           0 :             auto llmqType = sigShare.getLlmqType();
     493             : 
     494           0 :             auto k = std::make_pair(llmqType, sigShare.getQuorumHash());
     495           0 :             if (retQuorums.count(k) != 0) {
     496           0 :                 continue;
     497             :             }
     498             : 
     499           0 :             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           0 :             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           0 :             retQuorums.try_emplace(k, quorum);
     509           0 :         }
     510             :     }
     511             : 
     512           0 :     return more_work;
     513           0 : }
     514             : 
     515             : // It's ensured that no duplicates are passed to this method
     516           0 : 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           0 :     cxxtimer::Timer t(true);
     521           0 :     std::vector<std::shared_ptr<CRecoveredSig>> recovered_sigs;
     522           0 :     for (const auto& sigShare : sigSharesToProcess) {
     523           0 :         auto quorumKey = std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash());
     524           0 :         auto rs = ProcessSigShare(sigShare, quorums.at(quorumKey));
     525           0 :         if (rs != nullptr) {
     526           0 :             recovered_sigs.emplace_back(std::move(rs));
     527           0 :         }
     528           0 :     }
     529           0 :     t.stop();
     530             : 
     531           0 :     LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- processed sigShare batch. shares=%d, time=%ds\n", __func__,
     532             :              sigSharesToProcess.size(), t.count());
     533           0 :     return recovered_sigs;
     534           0 : }
     535             : 
     536             : // sig shares are already verified when entering this method
     537           0 : std::shared_ptr<CRecoveredSig> CSigSharesManager::ProcessSigShare(const CSigShare& sigShare, const CQuorumCPtr& quorum)
     538             : {
     539           0 :     auto llmqType = quorum->params.type;
     540             : 
     541           0 :     const bool isAllMembersConnectedEnabled = IsAllMembersConnectedEnabled(llmqType, m_sporkman);
     542             : 
     543             :     // prepare node set for direct-push in case this is our sig share
     544           0 :     std::vector<NodeId> quorumNodes;
     545           0 :     if (!isAllMembersConnectedEnabled &&
     546           0 :         sigShare.getQuorumMember() == quorum->GetMemberIndex(m_mn_activeman.GetProTxHash())) {
     547           0 :         quorumNodes = m_connman.GetMasternodeQuorumNodes(sigShare.getLlmqType(), sigShare.getQuorumHash());
     548           0 :     }
     549             : 
     550           0 :     if (sigman.HasRecoveredSigForId(llmqType, sigShare.getId())) {
     551           0 :         return nullptr;
     552             :     }
     553             : 
     554           0 :     bool canTryRecovery = false;
     555             :     {
     556           0 :         LOCK(cs);
     557             : 
     558           0 :         if (!sigShares.Add(sigShare.GetKey(), sigShare)) {
     559           0 :             return nullptr;
     560             :         }
     561           0 :         if (!isAllMembersConnectedEnabled) {
     562           0 :             sigSharesQueuedToAnnounce.Add(sigShare.GetKey(), true);
     563           0 :         }
     564             : 
     565             :         // Update the time we've seen the last sigShare
     566           0 :         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           0 :         for (auto otherNodeId : quorumNodes) {
     571           0 :             auto& nodeState = nodeStates[otherNodeId];
     572           0 :             auto& session = nodeState.GetOrCreateSessionFromShare(sigShare);
     573           0 :             session.quorum = quorum;
     574           0 :             session.requested.Set(sigShare.getQuorumMember(), true);
     575           0 :             session.knows.Set(sigShare.getQuorumMember(), true);
     576             :         }
     577             : 
     578           0 :         size_t sigShareCount = sigShares.CountForSignHash(sigShare.GetSignHash());
     579           0 :         if (sigShareCount >= size_t(quorum->params.threshold)) {
     580           0 :             canTryRecovery = true;
     581           0 :         }
     582           0 :     }
     583           0 :     if (!canTryRecovery) return nullptr;
     584             : 
     585           0 :     return TryRecoverSig(*quorum, sigShare.getId(), sigShare.getMsgHash());
     586           0 : }
     587             : 
     588           0 : std::shared_ptr<CRecoveredSig> CSigSharesManager::TryRecoverSig(const CQuorum& quorum, const uint256& id,
     589             :                                                                 const uint256& msgHash)
     590             : {
     591           0 :     if (sigman.HasRecoveredSigForId(quorum.params.type, id)) {
     592           0 :         return nullptr;
     593             :     }
     594             : 
     595           0 :     std::vector<CBLSSignature> sigSharesForRecovery;
     596           0 :     std::vector<CBLSId> idsForRecovery;
     597             :     {
     598           0 :         LOCK(cs);
     599             : 
     600           0 :         auto signHash = SignHash(quorum.params.type, quorum.qc->quorumHash, id, msgHash).Get();
     601           0 :         const auto* sigSharesForSignHash = sigShares.GetAllForSignHash(signHash);
     602           0 :         if (sigSharesForSignHash == nullptr) {
     603           0 :             return nullptr;
     604             :         }
     605             : 
     606           0 :         std::shared_ptr<CRecoveredSig> singleMemberRecoveredSig;
     607           0 :         if (quorum.params.is_single_member()) {
     608           0 :             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           0 :             const auto& sigShare = sigSharesForSignHash->begin()->second;
     616           0 :             CBLSSignature recoveredSig = sigShare.sigShare.Get();
     617           0 :             LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recover single-node signature. id=%s, msgHash=%s\n",
     618             :                      __func__, id.ToString(), msgHash.ToString());
     619             : 
     620           0 :             singleMemberRecoveredSig = std::make_shared<CRecoveredSig>(quorum.params.type, quorum.qc->quorumHash, id, msgHash,
     621             :                                                       recoveredSig);
     622           0 :         }
     623             : 
     624           0 :         sigSharesForRecovery.reserve((size_t) quorum.params.threshold);
     625           0 :         idsForRecovery.reserve((size_t) quorum.params.threshold);
     626           0 :         for (auto it = sigSharesForSignHash->begin(); it != sigSharesForSignHash->end() && sigSharesForRecovery.size() < size_t(quorum.params.threshold); ++it) {
     627           0 :             const auto& sigShare = it->second;
     628           0 :             sigSharesForRecovery.emplace_back(sigShare.sigShare.Get());
     629           0 :             idsForRecovery.emplace_back(quorum.members[sigShare.getQuorumMember()]->proTxHash);
     630           0 :         }
     631             : 
     632             :         // check if we can recover the final signature
     633           0 :         if (sigSharesForRecovery.size() < size_t(quorum.params.threshold)) {
     634           0 :             return nullptr;
     635             :         }
     636           0 :         if (quorum.params.is_single_member()) {
     637           0 :             return singleMemberRecoveredSig; // end of single-quorum processing
     638             :         }
     639           0 :     }
     640             : 
     641             :     // now recover it
     642           0 :     cxxtimer::Timer t(true);
     643           0 :     CBLSSignature recoveredSig;
     644           0 :     if (!recoveredSig.Recover(sigSharesForRecovery, idsForRecovery)) {
     645           0 :         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           0 :         return nullptr;
     648             :     }
     649             : 
     650           0 :     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           0 :     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           0 :     if (((recoveredSigsCounter++) % 100) == 0) {
     659           0 :         auto signHash = rs->buildSignHash();
     660           0 :         bool valid = recoveredSig.VerifyInsecure(quorum.qc->quorumPublicKey, signHash.Get());
     661           0 :         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           0 :     }
     668           0 :     return rs;
     669           0 : }
     670             : 
     671           0 : CDeterministicMNCPtr CSigSharesManager::SelectMemberForRecovery(const CQuorum& quorum, const uint256 &id, int attempt)
     672             : {
     673           0 :     assert(attempt < quorum.params.recoveryMembers);
     674             : 
     675           0 :     std::vector<std::pair<uint256, CDeterministicMNCPtr>> v;
     676           0 :     v.reserve(quorum.members.size());
     677           0 :     for (const auto& dmn : quorum.members) {
     678           0 :         auto h = ::SerializeHash(std::make_pair(dmn->proTxHash, id));
     679           0 :         v.emplace_back(h, dmn);
     680             :     }
     681           0 :     std::sort(v.begin(), v.end());
     682             : 
     683           0 :     return v[attempt % v.size()].second;
     684           0 : }
     685             : 
     686           0 : 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           0 :     AssertLockNotHeld(cs_pendingSigns);
     691             : 
     692           0 :     if (m_mn_activeman.GetProTxHash().IsNull()) return false;
     693             : 
     694           0 :     auto quorum = [&]() {
     695           0 :         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           0 :             const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     703           0 :             assert(llmq_params_opt.has_value());
     704           0 :             return SelectQuorumForSigning(llmq_params_opt.value(), m_chainman.ActiveChain(), qman, id);
     705             :         } else {
     706           0 :             return qman.GetQuorum(llmqType, quorumHash);
     707             :         }
     708           0 :     }();
     709             : 
     710           0 :     if (!quorum) {
     711           0 :         LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__,
     712             :                  id.ToString(), msgHash.ToString());
     713           0 :         return false;
     714             :     }
     715             : 
     716           0 :     if (!quorum->IsValidMember(m_mn_activeman.GetProTxHash())) {
     717           0 :         return false;
     718             :     }
     719             : 
     720             :     {
     721           0 :         auto& db = sigman.GetDb();
     722           0 :         bool hasVoted = db.HasVotedOnId(llmqType, id);
     723           0 :         if (hasVoted) {
     724           0 :             uint256 prevMsgHash;
     725           0 :             db.GetVoteForId(llmqType, id, prevMsgHash);
     726           0 :             if (msgHash != prevMsgHash) {
     727           0 :                 if (allowDiffMsgHashSigning) {
     728           0 :                     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           0 :                     hasVoted = false;
     732           0 :                 } else {
     733           0 :                     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           0 :                     return false;
     737             :                 }
     738           0 :             } else if (allowReSign) {
     739           0 :                 LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
     740             :                          id.ToString(), prevMsgHash.ToString());
     741           0 :             } else {
     742           0 :                 LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
     743             :                          id.ToString(), prevMsgHash.ToString());
     744           0 :                 return false;
     745             :             }
     746           0 :         }
     747             : 
     748           0 :         if (db.HasRecoveredSigForId(llmqType, id)) {
     749             :             // no need to sign it if we already have a recovered sig
     750           0 :             return true;
     751             :         }
     752           0 :         if (!hasVoted) {
     753           0 :             db.WriteVoteForId(llmqType, id, msgHash);
     754           0 :         }
     755             :     }
     756             : 
     757           0 :     if (allowReSign) {
     758             :         // make us re-announce all known shares (other nodes might have run into a timeout)
     759           0 :         ForceReAnnouncement(*quorum, llmqType, id, msgHash);
     760           0 :     }
     761           0 :     AsyncSign(std::move(quorum), id, msgHash);
     762             : 
     763           0 :     return true;
     764           0 : }
     765             : 
     766           0 : void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToRequest)
     767             : {
     768           0 :     AssertLockHeld(cs);
     769             : 
     770           0 :     int64_t now = GetTime<std::chrono::seconds>().count();
     771           0 :     const size_t maxRequestsForNode = 32;
     772             : 
     773             :     // avoid requesting from same nodes all the time
     774           0 :     std::vector<NodeId> shuffledNodeIds;
     775           0 :     shuffledNodeIds.reserve(nodeStates.size());
     776           0 :     for (const auto& [nodeId, nodeState] : nodeStates) {
     777           0 :         if (nodeState.sessions.empty()) {
     778           0 :             continue;
     779             :         }
     780           0 :         shuffledNodeIds.emplace_back(nodeId);
     781             :     }
     782           0 :     Shuffle(shuffledNodeIds.begin(), shuffledNodeIds.end(), rnd);
     783             : 
     784           0 :     for (const auto& nodeId : shuffledNodeIds) {
     785           0 :         auto& nodeState = nodeStates[nodeId];
     786             : 
     787           0 :         if (nodeState.banned) {
     788           0 :             continue;
     789             :         }
     790             : 
     791           0 :         nodeState.requestedSigShares.EraseIf([&now, &nodeId](const SigShareKey& k, int64_t t) {
     792           0 :             if (now - t >= SIG_SHARE_REQUEST_TIMEOUT) {
     793             :                 // timeout while waiting for this one, so retry it with another node
     794           0 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::CollectSigSharesToRequest -- timeout while waiting for %s-%d, node=%d\n",
     795             :                          k.first.ToString(), k.second, nodeId);
     796           0 :                 return true;
     797             :             }
     798           0 :             return false;
     799           0 :         });
     800             : 
     801           0 :         decltype(sigSharesToRequest.begin()->second)* invMap = nullptr;
     802             : 
     803           0 :         for (auto& [signHash, session] : nodeState.sessions) {
     804           0 :             if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
     805           0 :                 continue;
     806             :             }
     807             : 
     808           0 :             if (sigman.HasRecoveredSigForSession(signHash)) {
     809           0 :                 continue;
     810             :             }
     811             : 
     812           0 :             for (const auto i : util::irange(session.announced.inv.size())) {
     813           0 :                 if (!session.announced.inv[i]) {
     814           0 :                     continue;
     815             :                 }
     816           0 :                 auto k = std::make_pair(signHash, (uint16_t) i);
     817           0 :                 if (sigShares.Has(k)) {
     818             :                     // we already have it
     819           0 :                     session.announced.inv[i] = false;
     820           0 :                     continue;
     821             :                 }
     822           0 :                 if (nodeState.requestedSigShares.Size() >= maxRequestsForNode) {
     823             :                     // too many pending requests for this node
     824           0 :                     break;
     825             :                 }
     826           0 :                 if (auto *const p = sigSharesRequested.Get(k)) {
     827           0 :                     if (now - p->second >= SIG_SHARE_REQUEST_TIMEOUT && nodeId != p->first) {
     828             :                         // other node timed out, re-request from this node
     829           0 :                         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           0 :                     } else {
     832           0 :                         continue;
     833             :                     }
     834           0 :                 }
     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           0 :                 nodeState.requestedSigShares.Add(k, now);
     839             : 
     840             :                 // don't request it from other nodes until a timeout happens
     841           0 :                 auto& r = sigSharesRequested.GetOrAdd(k);
     842           0 :                 r.first = nodeId;
     843           0 :                 r.second = now;
     844             : 
     845           0 :                 if (invMap == nullptr) {
     846           0 :                     invMap = &sigSharesToRequest[nodeId];
     847           0 :                 }
     848           0 :                 auto& inv = (*invMap)[signHash];
     849           0 :                 if (inv.inv.empty()) {
     850           0 :                     const auto& llmq_params_opt = Params().GetLLMQ(session.llmqType);
     851           0 :                     assert(llmq_params_opt.has_value());
     852           0 :                     inv.Init(llmq_params_opt->size);
     853           0 :                 }
     854           0 :                 inv.inv[k.second] = true;
     855             : 
     856             :                 // don't request it again from this node
     857           0 :                 session.announced.inv[i] = false;
     858             :             }
     859             :         }
     860             :     }
     861           0 : }
     862             : 
     863           0 : void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>>& sigSharesToSend)
     864             : {
     865           0 :     AssertLockHeld(cs);
     866             : 
     867           0 :     for (auto& [nodeId, nodeState] : nodeStates) {
     868           0 :         if (nodeState.banned) {
     869           0 :             continue;
     870             :         }
     871             : 
     872           0 :         decltype(sigSharesToSend.begin()->second)* sigSharesToSend2 = nullptr;
     873             : 
     874           0 :         for (auto& [signHash, session] : nodeState.sessions) {
     875           0 :             if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
     876           0 :                 continue;
     877             :             }
     878             : 
     879           0 :             if (sigman.HasRecoveredSigForSession(signHash)) {
     880           0 :                 continue;
     881             :             }
     882             : 
     883           0 :             CBatchedSigShares batchedSigShares;
     884             : 
     885           0 :             for (const auto i : util::irange(session.requested.inv.size())) {
     886           0 :                 if (!session.requested.inv[i]) {
     887           0 :                     continue;
     888             :                 }
     889           0 :                 session.requested.inv[i] = false;
     890             : 
     891           0 :                 auto k = std::make_pair(signHash, (uint16_t)i);
     892           0 :                 const CSigShare* sigShare = sigShares.Get(k);
     893           0 :                 if (sigShare == nullptr) {
     894             :                     // he requested something we don't have
     895           0 :                     session.requested.inv[i] = false;
     896           0 :                     continue;
     897             :                 }
     898             : 
     899           0 :                 batchedSigShares.sigShares.emplace_back((uint16_t)i, sigShare->sigShare);
     900             :             }
     901             : 
     902           0 :             if (!batchedSigShares.sigShares.empty()) {
     903           0 :                 if (sigSharesToSend2 == nullptr) {
     904             :                     // only create the map if we actually add a batched sig
     905           0 :                     sigSharesToSend2 = &sigSharesToSend[nodeId];
     906           0 :                 }
     907           0 :                 sigSharesToSend2->try_emplace(signHash, std::move(batchedSigShares));
     908           0 :             }
     909           0 :         }
     910             :     }
     911           0 : }
     912             : 
     913           0 : void CSigSharesManager::CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes)
     914             : {
     915           0 :     AssertLockHeld(cs);
     916             : 
     917           0 :     Uint256HashMap<CNode*> proTxToNode;
     918           0 :     for (const auto& pnode : vNodes) {
     919           0 :         auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
     920           0 :         if (verifiedProRegTxHash.IsNull()) {
     921           0 :             continue;
     922             :         }
     923           0 :         proTxToNode.try_emplace(verifiedProRegTxHash, pnode);
     924             :     }
     925             : 
     926           0 :     auto curTime = GetTime<std::chrono::milliseconds>().count();
     927             : 
     928           0 :     for (auto& [_, signedSession] : signedSessions) {
     929           0 :         if (!IsAllMembersConnectedEnabled(signedSession.quorum->params.type, m_sporkman)) {
     930           0 :             continue;
     931             :         }
     932             : 
     933           0 :         if (signedSession.attempt >= signedSession.quorum->params.recoveryMembers) {
     934           0 :             continue;
     935             :         }
     936             : 
     937           0 :         if (curTime >= signedSession.nextAttemptTime) {
     938           0 :             int64_t waitTime = exp2(signedSession.attempt) * EXP_SEND_FOR_RECOVERY_TIMEOUT;
     939           0 :             waitTime = std::min(MAX_SEND_FOR_RECOVERY_TIMEOUT, waitTime);
     940           0 :             signedSession.nextAttemptTime = curTime + waitTime;
     941           0 :             auto dmn = SelectMemberForRecovery(*signedSession.quorum, signedSession.sigShare.getId(), signedSession.attempt);
     942           0 :             signedSession.attempt++;
     943             : 
     944           0 :             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           0 :             auto it = proTxToNode.find(dmn->proTxHash);
     948           0 :             if (it == proTxToNode.end()) {
     949           0 :                 continue;
     950             :             }
     951             : 
     952           0 :             auto& m = sigSharesToSend[it->second->GetId()];
     953           0 :             m.emplace_back(signedSession.sigShare);
     954           0 :         }
     955             :     }
     956           0 : }
     957             : 
     958           0 : void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToAnnounce)
     959             : {
     960           0 :     AssertLockHeld(cs);
     961             : 
     962           0 :     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           0 :     sigSharesQueuedToAnnounce.ForEach([this, &quorumNodesMap, &sigSharesToAnnounce](const SigShareKey& sigShareKey,
     967             :                                                                                     bool) NO_THREAD_SAFETY_ANALYSIS {
     968           0 :         AssertLockHeld(cs);
     969           0 :         const auto& signHash = sigShareKey.first;
     970           0 :         auto quorumMember = sigShareKey.second;
     971           0 :         const CSigShare* sigShare = sigShares.Get(sigShareKey);
     972           0 :         if (sigShare == nullptr) {
     973           0 :             return;
     974             :         }
     975             : 
     976             :         // announce to the nodes which we know through the intra-quorum-communication system
     977           0 :         auto quorumKey = std::make_pair(sigShare->getLlmqType(), sigShare->getQuorumHash());
     978           0 :         auto it = quorumNodesMap.find(quorumKey);
     979           0 :         if (it == quorumNodesMap.end()) {
     980           0 :             auto nodeIds = m_connman.GetMasternodeQuorumNodes(quorumKey.first, quorumKey.second);
     981           0 :             it = quorumNodesMap.emplace(std::piecewise_construct, std::forward_as_tuple(quorumKey), std::forward_as_tuple(nodeIds.begin(), nodeIds.end())).first;
     982           0 :         }
     983             : 
     984           0 :         const auto& quorumNodes = it->second;
     985             : 
     986           0 :         for (const auto& nodeId : quorumNodes) {
     987           0 :             auto& nodeState = nodeStates[nodeId];
     988             : 
     989           0 :             if (nodeState.banned) {
     990           0 :                 continue;
     991             :             }
     992             : 
     993           0 :             auto& session = nodeState.GetOrCreateSessionFromShare(*sigShare);
     994             : 
     995           0 :             if (session.knows.inv[quorumMember]) {
     996             :                 // he already knows that one
     997           0 :                 continue;
     998             :             }
     999             : 
    1000           0 :             auto& inv = sigSharesToAnnounce[nodeId][signHash];
    1001           0 :             if (inv.inv.empty()) {
    1002           0 :                 const auto& llmq_params_opt = Params().GetLLMQ(sigShare->getLlmqType());
    1003           0 :                 assert(llmq_params_opt.has_value());
    1004           0 :                 inv.Init(llmq_params_opt->size);
    1005           0 :             }
    1006           0 :             inv.inv[quorumMember] = true;
    1007           0 :             session.knows.inv[quorumMember] = true;
    1008             :         }
    1009           0 :     });
    1010             : 
    1011             :     // don't announce these anymore
    1012           0 :     sigSharesQueuedToAnnounce.Clear();
    1013           0 : }
    1014             : 
    1015           0 : bool CSigSharesManager::SendMessages()
    1016             : {
    1017           0 :     std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToRequest;
    1018           0 :     std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>> sigShareBatchesToSend;
    1019           0 :     std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesToSend;
    1020           0 :     std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToAnnounce;
    1021           0 :     std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
    1022             : 
    1023           0 :     auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
    1024           0 :         AssertLockHeld(cs);
    1025           0 :         auto& nodeState = nodeStates[nodeId];
    1026           0 :         auto* session = nodeState.GetSessionBySignHash(signHash);
    1027           0 :         assert(session);
    1028           0 :         while (session->sendSessionId == UNINITIALIZED_SESSION_ID) {
    1029           0 :             const uint32_t session_id{GetRand<uint32_t>()};
    1030           0 :             if (std::ranges::all_of(nodeState.sessions,
    1031           0 :                                     [&session_id](const auto& s) { return s.second.sendSessionId != session_id; })) {
    1032             :                 // No session is using this id yet
    1033           0 :                 session->sendSessionId = session_id;
    1034           0 :                 sigSessionAnnouncements[nodeId].emplace_back(
    1035           0 :                     CSigSesAnn(/*sessionId=*/session->sendSessionId, /*llmqType=*/session->llmqType,
    1036           0 :                                /*quorumHash=*/session->quorumHash, /*id=*/session->id, /*msgHash=*/session->msgHash));
    1037           0 :             }
    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           0 :         return session->sendSessionId;
    1042             :     };
    1043             : 
    1044           0 :     const CConnman::NodesSnapshot snap{m_connman, /* cond = */ CConnman::FullyConnectedOnly};
    1045             :     {
    1046           0 :         LOCK(cs);
    1047           0 :         CollectSigSharesToRequest(sigSharesToRequest);
    1048           0 :         CollectSigSharesToSend(sigShareBatchesToSend);
    1049           0 :         CollectSigSharesToAnnounce(sigSharesToAnnounce);
    1050           0 :         CollectSigSharesToSendConcentrated(sigSharesToSend, snap.Nodes());
    1051             : 
    1052           0 :         for (auto& [nodeId, sigShareMap] : sigSharesToRequest) {
    1053           0 :             for (auto& [hash, sigShareInv] : sigShareMap) {
    1054           0 :                 sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
    1055             :             }
    1056             :         }
    1057           0 :         for (auto& [nodeId, sigShareBatchesMap] : sigShareBatchesToSend) {
    1058           0 :             for (auto& [hash, sigShareBatch] : sigShareBatchesMap) {
    1059           0 :                 sigShareBatch.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
    1060             :             }
    1061             :         }
    1062           0 :         for (auto& [nodeId, sigShareMap] : sigSharesToAnnounce) {
    1063           0 :             for (auto& [hash, sigShareInv] : sigShareMap) {
    1064           0 :                 sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
    1065             :             }
    1066             :         }
    1067           0 :     }
    1068             : 
    1069           0 :     bool didSend = false;
    1070             : 
    1071           0 :     for (auto& pnode : snap.Nodes()) {
    1072           0 :         CNetMsgMaker msgMaker(pnode->GetCommonVersion());
    1073             : 
    1074           0 :         if (const auto it1 = sigSessionAnnouncements.find(pnode->GetId()); it1 != sigSessionAnnouncements.end()) {
    1075           0 :             std::vector<CSigSesAnn> msgs;
    1076           0 :             msgs.reserve(it1->second.size());
    1077           0 :             for (auto& sigSesAnn : it1->second) {
    1078           0 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSESANN signHash=%s, sessionId=%d, node=%d\n",
    1079             :                          sigSesAnn.buildSignHash().ToString(), sigSesAnn.getSessionId(), pnode->GetId());
    1080           0 :                 msgs.emplace_back(sigSesAnn);
    1081           0 :                 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           0 :             if (!msgs.empty()) {
    1088           0 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
    1089           0 :                 didSend = true;
    1090           0 :             }
    1091           0 :         }
    1092             : 
    1093           0 :         if (const auto it = sigSharesToRequest.find(pnode->GetId()); it != sigSharesToRequest.end()) {
    1094           0 :             std::vector<CSigSharesInv> msgs;
    1095           0 :             for (const auto& [signHash, inv] : it->second) {
    1096           0 :                 assert(inv.CountSet() != 0);
    1097           0 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QGETSIGSHARES signHash=%s, inv={%s}, node=%d\n",
    1098             :                          signHash.ToString(), inv.ToString(), pnode->GetId());
    1099           0 :                 msgs.emplace_back(inv);
    1100           0 :                 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           0 :             if (!msgs.empty()) {
    1107           0 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
    1108           0 :                 didSend = true;
    1109           0 :             }
    1110           0 :         }
    1111             : 
    1112           0 :         if (const auto jt = sigShareBatchesToSend.find(pnode->GetId()); jt != sigShareBatchesToSend.end()) {
    1113           0 :             size_t totalSigsCount = 0;
    1114           0 :             std::vector<CBatchedSigShares> msgs;
    1115           0 :             for (const auto& [signHash, inv] : jt->second) {
    1116           0 :                 assert(!inv.sigShares.empty());
    1117           0 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QBSIGSHARES signHash=%s, inv={%s}, node=%d\n",
    1118             :                          signHash.ToString(), inv.ToInvString(), pnode->GetId());
    1119           0 :                 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           0 :                 totalSigsCount += inv.sigShares.size();
    1126           0 :                 msgs.emplace_back(inv);
    1127             :             }
    1128           0 :             if (!msgs.empty()) {
    1129           0 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, std::move(msgs)));
    1130           0 :                 didSend = true;
    1131           0 :             }
    1132           0 :         }
    1133             : 
    1134           0 :         if (const auto kt = sigSharesToAnnounce.find(pnode->GetId()); kt != sigSharesToAnnounce.end()) {
    1135           0 :             std::vector<CSigSharesInv> msgs;
    1136           0 :             for (const auto& [signHash, inv] : kt->second) {
    1137           0 :                 assert(inv.CountSet() != 0);
    1138           0 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARESINV signHash=%s, inv={%s}, node=%d\n",
    1139             :                          signHash.ToString(), inv.ToString(), pnode->GetId());
    1140           0 :                 msgs.emplace_back(inv);
    1141           0 :                 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           0 :             if (!msgs.empty()) {
    1148           0 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
    1149           0 :                 didSend = true;
    1150           0 :             }
    1151           0 :         }
    1152             : 
    1153           0 :         auto lt = sigSharesToSend.find(pnode->GetId());
    1154           0 :         if (lt != sigSharesToSend.end()) {
    1155           0 :             std::vector<CSigShare> msgs;
    1156           0 :             for (auto& sigShare : lt->second) {
    1157           0 :                 LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARE signHash=%s, node=%d\n",
    1158             :                          sigShare.GetSignHash().ToString(), pnode->GetId());
    1159           0 :                 msgs.emplace_back(std::move(sigShare));
    1160           0 :                 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           0 :             if (!msgs.empty()) {
    1167           0 :                 m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
    1168           0 :                 didSend = true;
    1169           0 :             }
    1170           0 :         }
    1171             :     }
    1172             : 
    1173           0 :     return didSend;
    1174           0 : }
    1175             : 
    1176           0 : bool CSigSharesManager::GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo)
    1177             : {
    1178           0 :     LOCK(cs);
    1179           0 :     return nodeStates[nodeId].GetSessionInfoByRecvId(sessionId, retInfo);
    1180           0 : }
    1181             : 
    1182           0 : CSigShare CSigSharesManager::RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const std::pair<uint16_t, CBLSLazySignature>& in)
    1183             : {
    1184           0 :     const auto& [member, sig] = in;
    1185           0 :     CSigShare sigShare(session.llmqType, session.quorumHash, session.id, session.msgHash, member, sig);
    1186           0 :     sigShare.UpdateKey();
    1187           0 :     return sigShare;
    1188           0 : }
    1189             : 
    1190           0 : void CSigSharesManager::Cleanup()
    1191             : {
    1192           0 :     constexpr auto CLEANUP_INTERVAL{5s};
    1193           0 :     if (!cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) {
    1194           0 :         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           0 :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
    1202             : 
    1203             :     {
    1204           0 :         LOCK(cs);
    1205           0 :         sigShares.ForEach([&quorums](const SigShareKey&, const CSigShare& sigShare) {
    1206           0 :             quorums.try_emplace(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash()), nullptr);
    1207           0 :         });
    1208           0 :     }
    1209             : 
    1210             :     // Find quorums which became inactive
    1211           0 :     for (auto it = quorums.begin(); it != quorums.end(); ) {
    1212           0 :         if (IsQuorumActive(it->first.first, qman, it->first.second)) {
    1213           0 :             auto quorum = qman.GetQuorum(it->first.first, it->first.second);
    1214           0 :             if (quorum) {
    1215           0 :                 it->second = quorum;
    1216           0 :                 ++it;
    1217           0 :                 continue;
    1218             :             }
    1219           0 :         }
    1220           0 :         it = quorums.erase(it);
    1221             :     }
    1222             : 
    1223             :     {
    1224             :         // Now delete sessions which are for inactive quorums
    1225           0 :         LOCK(cs);
    1226           0 :         Uint256HashSet inactiveQuorumSessions;
    1227           0 :         sigShares.ForEach([&quorums, &inactiveQuorumSessions](const SigShareKey&, const CSigShare& sigShare) {
    1228           0 :             if (quorums.count(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash())) == 0) {
    1229           0 :                 inactiveQuorumSessions.emplace(sigShare.GetSignHash());
    1230           0 :             }
    1231           0 :         });
    1232           0 :         for (const auto& signHash : inactiveQuorumSessions) {
    1233           0 :             RemoveSigSharesForSession(signHash);
    1234             :         }
    1235           0 :     }
    1236             : 
    1237             :     {
    1238           0 :         LOCK(cs);
    1239             : 
    1240             :         // Remove sessions which were successfully recovered
    1241           0 :         Uint256HashSet doneSessions;
    1242           0 :         sigShares.ForEach([&doneSessions, this](const SigShareKey&, const CSigShare& sigShare) {
    1243           0 :             if (doneSessions.count(sigShare.GetSignHash()) != 0) {
    1244           0 :                 return;
    1245             :             }
    1246           0 :             if (sigman.HasRecoveredSigForSession(sigShare.GetSignHash())) {
    1247           0 :                 doneSessions.emplace(sigShare.GetSignHash());
    1248           0 :             }
    1249           0 :         });
    1250           0 :         for (const auto& signHash : doneSessions) {
    1251           0 :             RemoveSigSharesForSession(signHash);
    1252             :         }
    1253             : 
    1254             :         // Remove sessions which timed out
    1255           0 :         Uint256HashSet timeoutSessions;
    1256           0 :         int64_t now = GetTime<std::chrono::seconds>().count();
    1257           0 :         for (const auto& [signHash, lastSeenTime] : timeSeenForSessions) {
    1258           0 :             if (now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
    1259           0 :                 timeoutSessions.emplace(signHash);
    1260           0 :             }
    1261             :         }
    1262           0 :         for (const auto& signHash : timeoutSessions) {
    1263             : 
    1264           0 :             if (const size_t count = sigShares.CountForSignHash(signHash); count > 0) {
    1265           0 :                 const auto* m = sigShares.GetAllForSignHash(signHash);
    1266           0 :                 assert(m);
    1267             : 
    1268           0 :                 const auto& oneSigShare = m->begin()->second;
    1269             : 
    1270           0 :                 std::string strMissingMembers;
    1271           0 :                 if (LogAcceptDebug(BCLog::LLMQ_SIGS)) {
    1272           0 :                     if (const auto quorumIt = quorums.find(std::make_pair(oneSigShare.getLlmqType(), oneSigShare.getQuorumHash())); quorumIt != quorums.end()) {
    1273           0 :                         const auto& quorum = quorumIt->second;
    1274           0 :                         for (const auto i : util::irange(quorum->members.size())) {
    1275           0 :                             if (m->count((uint16_t)i) == 0) {
    1276           0 :                                 const auto& dmn = quorum->members[i];
    1277           0 :                                 strMissingMembers += strprintf("\n  %s", dmn->proTxHash.ToString());
    1278           0 :                             }
    1279             :                         }
    1280           0 :                     }
    1281           0 :                 }
    1282             : 
    1283           0 :                 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           0 :             } 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           0 :             RemoveSigSharesForSession(signHash);
    1294             :         }
    1295           0 :     }
    1296             : 
    1297             :     // Find node states for peers that disappeared from CConnman
    1298           0 :     std::unordered_set<NodeId> nodeStatesToDelete;
    1299             :     {
    1300           0 :         LOCK(cs);
    1301           0 :         for (const auto& [nodeId, _] : nodeStates) {
    1302           0 :             nodeStatesToDelete.emplace(nodeId);
    1303             :         }
    1304           0 :     }
    1305           0 :     m_connman.ForEachNode([&nodeStatesToDelete](const CNode* pnode) { nodeStatesToDelete.erase(pnode->GetId()); });
    1306             : 
    1307             :     // Now delete these node states
    1308           0 :     LOCK(cs);
    1309           0 :     for (const auto& nodeId : nodeStatesToDelete) {
    1310           0 :         auto it = nodeStates.find(nodeId);
    1311           0 :         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           0 :         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           0 :         nodeStates.erase(nodeId);
    1322             :     }
    1323           0 : }
    1324             : 
    1325           0 : void CSigSharesManager::RemoveSigSharesForSession(const uint256& signHash)
    1326             : {
    1327           0 :     AssertLockHeld(cs);
    1328             : 
    1329           0 :     for (auto& [_, nodeState] : nodeStates) {
    1330           0 :         nodeState.RemoveSession(signHash);
    1331             :     }
    1332             : 
    1333           0 :     sigSharesRequested.EraseAllForSignHash(signHash);
    1334           0 :     sigSharesQueuedToAnnounce.EraseAllForSignHash(signHash);
    1335           0 :     sigShares.EraseAllForSignHash(signHash);
    1336           0 :     signedSessions.erase(signHash);
    1337           0 :     timeSeenForSessions.erase(signHash);
    1338           0 : }
    1339             : 
    1340           0 : void CSigSharesManager::RemoveNodesIf(std::function<bool(NodeId)> predicate)
    1341             : {
    1342           0 :     LOCK(cs);
    1343           0 :     for (auto it = nodeStates.begin(); it != nodeStates.end();) {
    1344           0 :         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           0 :             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           0 :             it = nodeStates.erase(it);
    1353           0 :         } else {
    1354           0 :             ++it;
    1355             :         }
    1356             :     }
    1357           0 : }
    1358             : 
    1359           0 : void CSigSharesManager::MarkAsBanned(NodeId nodeId)
    1360             : {
    1361           0 :     if (nodeId == -1) {
    1362           0 :         return;
    1363             :     }
    1364             : 
    1365           0 :     LOCK(cs);
    1366           0 :     auto it = nodeStates.find(nodeId);
    1367           0 :     if (it == nodeStates.end()) {
    1368           0 :         return;
    1369             :     }
    1370             : 
    1371           0 :     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           0 :     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           0 :     nodeState.requestedSigShares.Clear();
    1380           0 :     nodeState.banned = true;
    1381           0 : }
    1382             : 
    1383           0 : std::vector<PendingSignatureData> CSigSharesManager::DispatchPendingSigns()
    1384             : {
    1385             :     // Swap out entire vector to avoid lock thrashing
    1386           0 :     std::vector<PendingSignatureData> signs;
    1387             : 
    1388           0 :     LOCK(cs_pendingSigns);
    1389           0 :     signs.swap(pendingSigns);
    1390             : 
    1391           0 :     return signs;
    1392           0 : }
    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           0 : std::shared_ptr<CRecoveredSig> CSigSharesManager::SignAndProcessSingleShare(PendingSignatureData work)
    1403             : {
    1404           0 :     auto opt_sigShare = CreateSigShare(*work.quorum, work.id, work.msgHash);
    1405             : 
    1406           0 :     if (opt_sigShare.has_value() && opt_sigShare->sigShare.Get().IsValid()) {
    1407           0 :         auto& sigShare = *opt_sigShare;
    1408           0 :         auto rs = ProcessSigShare(sigShare, work.quorum);
    1409             : 
    1410           0 :         if (IsAllMembersConnectedEnabled(work.quorum->params.type, m_sporkman)) {
    1411           0 :             LOCK(cs);
    1412           0 :             auto& session = signedSessions[sigShare.GetSignHash()];
    1413           0 :             session.sigShare = std::move(sigShare);
    1414           0 :             session.quorum = work.quorum;
    1415           0 :             session.nextAttemptTime = 0;
    1416           0 :             session.attempt = 0;
    1417           0 :         }
    1418           0 :         return rs;
    1419           0 :     }
    1420           0 :     return nullptr;
    1421           0 : }
    1422             : 
    1423           0 : void CSigSharesManager::AsyncSign(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash)
    1424             : {
    1425           0 :     LOCK(cs_pendingSigns);
    1426           0 :     pendingSigns.emplace_back(std::move(quorum), id, msgHash);
    1427           0 : }
    1428             : 
    1429           0 : std::optional<CSigShare> CSigSharesManager::CreateSigShareForSingleMember(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
    1430             : {
    1431           0 :     cxxtimer::Timer t(true);
    1432           0 :     auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
    1433             : 
    1434           0 :     int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
    1435           0 :     if (memberIdx == -1) {
    1436             :         // this should really not happen (IsValidMember gave true)
    1437           0 :         return std::nullopt;
    1438             :     }
    1439             : 
    1440           0 :     CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
    1441           0 :     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           0 :     auto bls_scheme = bls::bls_legacy_scheme.load();
    1446           0 :     sigShare.sigShare.Set(m_mn_activeman.Sign(signHash, bls_scheme), bls_scheme);
    1447             : 
    1448           0 :     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           0 :     sigShare.UpdateKey();
    1456             : 
    1457           0 :     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           0 :     return sigShare;
    1464           0 : }
    1465             : 
    1466           0 : std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
    1467             : {
    1468           0 :     auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
    1469             : 
    1470           0 :     if (!quorum.IsValidMember(activeMasterNodeProTxHash)) {
    1471           0 :         return std::nullopt;
    1472             :     }
    1473             : 
    1474           0 :     if (quorum.params.is_single_member()) {
    1475           0 :         return CreateSigShareForSingleMember(quorum, id, msgHash);
    1476             :     }
    1477           0 :     cxxtimer::Timer t(true);
    1478           0 :     const CBLSSecretKey& skShare = quorum.GetSkShare();
    1479           0 :     if (!skShare.IsValid()) {
    1480           0 :         LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have our skShare for quorum %s\n", __func__, quorum.qc->quorumHash.ToString());
    1481           0 :         return std::nullopt;
    1482             :     }
    1483             : 
    1484           0 :     int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
    1485           0 :     if (memberIdx == -1) {
    1486             :         // this should really not happen (IsValidMember gave true)
    1487           0 :         return std::nullopt;
    1488             :     }
    1489             : 
    1490           0 :     CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
    1491           0 :     uint256 signHash = sigShare.buildSignHash().Get();
    1492             : 
    1493           0 :     auto bls_scheme = bls::bls_legacy_scheme.load();
    1494           0 :     sigShare.sigShare.Set(skShare.Sign(signHash, bls_scheme), bls_scheme);
    1495           0 :     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           0 :     sigShare.UpdateKey();
    1502             : 
    1503           0 :     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           0 :     return sigShare;
    1507           0 : }
    1508             : 
    1509             : // causes all known sigShares to be re-announced
    1510           0 : void CSigSharesManager::ForceReAnnouncement(const CQuorum& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
    1511             : {
    1512           0 :     if (IsAllMembersConnectedEnabled(llmqType, m_sporkman)) {
    1513           0 :         return;
    1514             :     }
    1515             : 
    1516           0 :     LOCK(cs);
    1517           0 :     auto signHash = SignHash(llmqType, quorum.qc->quorumHash, id, msgHash).Get();
    1518           0 :     if (const auto *const sigs = sigShares.GetAllForSignHash(signHash)) {
    1519           0 :         for (const auto& [quorumMemberIndex, _] : *sigs) {
    1520             :             // re-announce every sigshare to every node
    1521           0 :             sigSharesQueuedToAnnounce.Add(std::make_pair(signHash, quorumMemberIndex), true);
    1522             :         }
    1523           0 :     }
    1524           0 :     for (auto& [_, nodeState] : nodeStates) {
    1525           0 :         auto* session = nodeState.GetSessionBySignHash(signHash);
    1526           0 :         if (session == nullptr) {
    1527           0 :             continue;
    1528             :         }
    1529             :         // pretend that the other node doesn't know about any shares so that we re-announce everything
    1530           0 :         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           0 :         session->sendSessionId = UNINITIALIZED_SESSION_ID;
    1533             :     }
    1534           0 : }
    1535             : 
    1536           0 : RecoveredSigResult CSigSharesManager::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
    1537             : {
    1538           0 :     auto signHash = recoveredSig.buildSignHash().Get();
    1539           0 :     LOCK(cs);
    1540           0 :     RemoveSigSharesForSession(signHash);
    1541           0 :     return std::monostate{};
    1542           0 : }
    1543             : } // namespace llmq

Generated by: LCOV version 1.16