LCOV - code coverage report
Current view: top level - src/llmq - signing_shares.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 139 141 98.6 %
Date: 2026-06-25 07:23:43 Functions: 125 126 99.2 %

          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             : #ifndef BITCOIN_LLMQ_SIGNING_SHARES_H
       6             : #define BITCOIN_LLMQ_SIGNING_SHARES_H
       7             : 
       8             : #include <bls/bls.h>
       9             : #include <evo/types.h>
      10             : #include <llmq/signhash.h>
      11             : #include <llmq/signing.h>
      12             : #include <util/std23.h>
      13             : 
      14             : #include <random.h>
      15             : #include <saltedhasher.h>
      16             : #include <serialize.h>
      17             : #include <sync.h>
      18             : #include <uint256.h>
      19             : #include <util/time.h>
      20             : 
      21             : #include <atomic>
      22             : #include <functional>
      23             : #include <limits>
      24             : #include <memory>
      25             : #include <optional>
      26             : #include <string>
      27             : #include <unordered_map>
      28             : #include <utility>
      29             : #include <vector>
      30             : 
      31             : class CActiveMasternodeManager;
      32             : class ChainstateManager;
      33             : class CNode;
      34             : class CConnman;
      35             : class CSporkManager;
      36             : 
      37             : namespace llmq
      38             : {
      39             : class CSigningManager;
      40             : 
      41             : // <signHash, quorumMember>
      42             : using SigShareKey = std::pair<uint256, uint16_t>;
      43             : 
      44             : constexpr uint32_t UNINITIALIZED_SESSION_ID{std::numeric_limits<uint32_t>::max()};
      45             : 
      46             : class CSigShare : virtual public CSigBase
      47             : {
      48             : protected:
      49       13462 :     uint16_t quorumMember{std::numeric_limits<uint16_t>::max()};
      50             : public:
      51             :     CBLSLazySignature sigShare;
      52             : 
      53             :     SigShareKey key;
      54             : 
      55      134179 :     [[nodiscard]] auto getQuorumMember() const {
      56      134179 :         return quorumMember;
      57             :     }
      58             : 
      59       73978 :     CSigShare(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash,
      60             :               uint16_t _quorumMember, const CBLSLazySignature& _sigShare) :
      61       36989 :                     CSigBase(_llmqType, _quorumHash, _id, _msgHash),
      62       36989 :                     quorumMember(_quorumMember),
      63      110967 :                     sigShare(_sigShare) {};
      64             : 
      65             :     // This should only be used for serialization
      66       26924 :     CSigShare() = default;
      67             : 
      68             : 
      69             : public:
      70             :     void UpdateKey();
      71      202506 :     const SigShareKey& GetKey() const
      72             :     {
      73      202506 :         return key;
      74             :     }
      75      254347 :     const uint256& GetSignHash() const
      76             :     {
      77      254347 :         assert(!key.first.IsNull());
      78      254347 :         return key.first;
      79             :     }
      80             : 
      81       38361 :     SERIALIZE_METHODS(CSigShare, obj)
      82             :     {
      83       12787 :         READWRITE(obj.llmqType, obj.quorumHash, obj.quorumMember, obj.id, obj.msgHash, obj.sigShare);
      84       19166 :         SER_READ(obj, obj.UpdateKey());
      85       12787 :     }
      86             : };
      87             : 
      88             : // Nodes will first announce a signing session with a sessionId to be used in all future P2P messages related to that
      89             : // session. We locally keep track of the mapping for each node. We also assign new sessionIds for outgoing sessions
      90             : // and send QSIGSESANN messages appropriately. All values except the max value for uint32_t are valid as sessionId
      91             : class CSigSesAnn : virtual public CSigBase
      92             : {
      93             : private:
      94       21816 :     uint32_t sessionId{UNINITIALIZED_SESSION_ID};
      95             : 
      96             : public:
      97       21836 :     CSigSesAnn(uint32_t _sessionId, Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id,
      98       21836 :                const uint256& _msgHash) : CSigBase(_llmqType, _quorumHash, _id, _msgHash), sessionId(_sessionId) {};
      99             :     // ONLY FOR SERIALIZATION
     100       43632 :     CSigSesAnn() = default;
     101             : 
     102             : 
     103             : 
     104      109094 :     [[nodiscard]] auto getSessionId() const {
     105      109094 :         return sessionId;
     106             :     }
     107             : 
     108      130941 :     SERIALIZE_METHODS(CSigSesAnn, obj)
     109             :     {
     110       43647 :         READWRITE(VARINT(obj.sessionId), obj.llmqType, obj.quorumHash, obj.id, obj.msgHash);
     111       43647 :     }
     112             : 
     113             :     [[nodiscard]] std::string ToString() const;
     114             : };
     115             : 
     116      166025 : class CSigSharesInv
     117             : {
     118             : public:
     119      166025 :     uint32_t sessionId{UNINITIALIZED_SESSION_ID};
     120             :     std::vector<bool> inv;
     121             : 
     122             : public:
     123       92037 :     SERIALIZE_METHODS(CSigSharesInv, obj)
     124             :     {
     125       30679 :         uint64_t invSize = obj.inv.size();
     126       30679 :         READWRITE(VARINT(obj.sessionId), COMPACTSIZE(invSize));
     127       30679 :         autobitset_t bitset = std::make_pair(obj.inv, (size_t)invSize);
     128       30679 :         READWRITE(AUTOBITSET(bitset));
     129       46012 :         SER_READ(obj, obj.inv = bitset.first);
     130       30679 :     }
     131             : 
     132             :     void Init(size_t size);
     133             :     void Set(uint16_t quorumMember, bool v);
     134             :     void SetAll(bool v);
     135             :     void Merge(const CSigSharesInv& inv2);
     136             : 
     137             :     [[nodiscard]] size_t CountSet() const;
     138             :     [[nodiscard]] std::string ToString() const;
     139             : };
     140             : 
     141             : // sent through the message QBSIGSHARES as a vector of multiple batches
     142      689638 : class CBatchedSigShares
     143             : {
     144             : public:
     145      689638 :     uint32_t sessionId{UNINITIALIZED_SESSION_ID};
     146             :     std::vector<std::pair<uint16_t, CBLSLazySignature>> sigShares;
     147             : 
     148             : public:
     149      113427 :     SERIALIZE_METHODS(CBatchedSigShares, obj)
     150             :     {
     151       37809 :         READWRITE(VARINT(obj.sessionId), obj.sigShares);
     152       37809 :     }
     153             : 
     154             :     [[nodiscard]] std::string ToInvString() const;
     155             : };
     156             : 
     157             : template<typename T>
     158             : class SigShareMap
     159             : {
     160             : private:
     161             :     Uint256HashMap<std::unordered_map<uint16_t, T>> internalMap;
     162             : 
     163             : public:
     164       97752 :     bool Add(const SigShareKey& k, const T& v)
     165             :     {
     166       97752 :         auto& m = internalMap[k.first];
     167       97752 :         return m.emplace(k.second, v).second;
     168             :     }
     169             : 
     170       41492 :     void Erase(const SigShareKey& k)
     171             :     {
     172       41492 :         auto it = internalMap.find(k.first);
     173       41492 :         if (it == internalMap.end()) {
     174       16702 :             return;
     175             :         }
     176       24790 :         it->second.erase(k.second);
     177       24790 :         if (it->second.empty()) {
     178       24755 :             internalMap.erase(it);
     179       24755 :         }
     180       41492 :     }
     181             : 
     182      216243 :     void Clear()
     183             :     {
     184      216243 :         internalMap.clear();
     185      216243 :     }
     186             : 
     187       58752 :     [[nodiscard]] bool Has(const SigShareKey& k) const
     188             :     {
     189       58752 :         auto it = internalMap.find(k.first);
     190       58752 :         if (it == internalMap.end()) {
     191        8866 :             return false;
     192             :         }
     193       49886 :         return it->second.count(k.second) != 0;
     194       58752 :     }
     195             : 
     196       48249 :     T* Get(const SigShareKey& k)
     197             :     {
     198       48249 :         auto it = internalMap.find(k.first);
     199       48249 :         if (it == internalMap.end()) {
     200        6230 :             return nullptr;
     201             :         }
     202             : 
     203       42019 :         auto jt = it->second.find(k.second);
     204       42019 :         if (jt == it->second.end()) {
     205         146 :             return nullptr;
     206             :         }
     207             : 
     208       41873 :         return &jt->second;
     209       48249 :     }
     210             : 
     211        3185 :     T& GetOrAdd(const SigShareKey& k)
     212             :     {
     213        3185 :         T* v = Get(k);
     214        3185 :         if (!v) {
     215        3183 :             Add(k, T());
     216        3183 :             v = Get(k);
     217        3183 :         }
     218        3185 :         return *v;
     219             :     }
     220             : 
     221       22840 :     const T* GetFirst() const
     222             :     {
     223       22840 :         if (internalMap.empty()) {
     224           0 :             return nullptr;
     225             :         }
     226       22840 :         return &internalMap.begin()->second.begin()->second;
     227       22840 :     }
     228             : 
     229        5681 :     [[nodiscard]] size_t Size() const
     230             :     {
     231        5681 :         return std23::ranges::fold_left(internalMap, size_t{0},
     232        3255 :                                         [](size_t s, const auto& p) { return s + p.second.size(); });
     233             :     }
     234             : 
     235       41470 :     [[nodiscard]] size_t CountForSignHash(const uint256& signHash) const
     236             :     {
     237       41470 :         auto it = internalMap.find(signHash);
     238       41470 :         if (it == internalMap.end()) {
     239           0 :             return 0;
     240             :         }
     241       41470 :         return it->second.size();
     242       41470 :     }
     243             : 
     244      987177 :     [[nodiscard]] bool Empty() const
     245             :     {
     246      987177 :         return internalMap.empty();
     247             :     }
     248             : 
     249       11463 :     const std::unordered_map<uint16_t, T>* GetAllForSignHash(const uint256& signHash) const
     250             :     {
     251       11463 :         auto it = internalMap.find(signHash);
     252       11463 :         if (it == internalMap.end()) {
     253         839 :             return nullptr;
     254             :         }
     255       10624 :         return &it->second;
     256       11463 :     }
     257             : 
     258      267466 :     void EraseAllForSignHash(const uint256& signHash)
     259             :     {
     260      267466 :         internalMap.erase(signHash);
     261      267466 :     }
     262             : 
     263             :     template<typename F>
     264      105697 :     void EraseIf(F&& f)
     265             :     {
     266      107164 :         for (auto it = internalMap.begin(); it != internalMap.end(); ) {
     267        1467 :             SigShareKey k;
     268        1467 :             k.first = it->first;
     269        3258 :             for (auto jt = it->second.begin(); jt != it->second.end(); ) {
     270        1791 :                 k.second = jt->first;
     271        1791 :                 if (f(k, jt->second)) {
     272          25 :                     jt = it->second.erase(jt);
     273          25 :                 } else {
     274        1766 :                     ++jt;
     275             :                 }
     276             :             }
     277        1467 :             if (it->second.empty()) {
     278          25 :                 it = internalMap.erase(it);
     279          25 :             } else {
     280        1442 :                 ++it;
     281             :             }
     282             :         }
     283      105697 :     }
     284             : 
     285             :     template<typename F>
     286      246509 :     void ForEach(F&& f)
     287             :     {
     288      306826 :         for (auto& p : internalMap) {
     289       60317 :             SigShareKey k;
     290       60317 :             k.first = p.first;
     291      151191 :             for (auto& p2 : p.second) {
     292       90874 :                 k.second = p2.first;
     293       90874 :                 f(k, p2.second);
     294             :             }
     295             :         }
     296      246509 :     }
     297             : };
     298             : 
     299        1770 : class CSigSharesNodeState
     300             : {
     301             : public:
     302             :     // Used to avoid holding locks too long
     303       34226 :     struct SessionInfo
     304             :     {
     305       34226 :         Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
     306             :         uint256 quorumHash;
     307             :         uint256 id;
     308             :         uint256 msgHash;
     309             :         llmq::SignHash signHash;
     310             : 
     311             :         CQuorumCPtr quorum;
     312             :     };
     313             : 
     314       32599 :     struct Session {
     315       32599 :         uint32_t recvSessionId{UNINITIALIZED_SESSION_ID};
     316       32599 :         uint32_t sendSessionId{UNINITIALIZED_SESSION_ID};
     317             : 
     318             :         Consensus::LLMQType llmqType;
     319             :         uint256 quorumHash;
     320             :         uint256 id;
     321             :         uint256 msgHash;
     322             :         llmq::SignHash signHash;
     323             : 
     324             :         CQuorumCPtr quorum;
     325             : 
     326             :         CSigSharesInv announced;
     327             :         CSigSharesInv requested;
     328             :         CSigSharesInv knows;
     329             :     };
     330             :     // TODO limit number of sessions per node
     331             :     Uint256HashMap<Session> sessions;
     332             : 
     333             :     std::unordered_map<uint32_t, Session*> sessionByRecvId;
     334             : 
     335             :     SigShareMap<CSigShare> pendingIncomingSigShares;
     336             :     SigShareMap<int64_t> requestedSigShares;
     337             : 
     338        1770 :     bool banned{false};
     339             : 
     340             :     Session& GetOrCreateSessionFromShare(const CSigShare& sigShare);
     341             :     Session& GetOrCreateSessionFromAnn(const CSigSesAnn& ann);
     342             :     Session* GetSessionBySignHash(const uint256& signHash);
     343             :     Session* GetSessionByRecvId(uint32_t sessionId);
     344             :     bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo);
     345             : 
     346             :     void RemoveSession(const uint256& signHash);
     347             : };
     348             : 
     349        7083 : class CSignedSession
     350             : {
     351             : public:
     352             :     CSigShare sigShare;
     353             :     CQuorumCPtr quorum;
     354             : 
     355        7083 :     int64_t nextAttemptTime{0};
     356        7083 :     int attempt{0};
     357             : };
     358             : 
     359             : struct PendingSignatureData {
     360             :     const CQuorumCPtr quorum;
     361             :     const uint256 id;
     362             :     const uint256 msgHash;
     363             : 
     364       36838 :     PendingSignatureData(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash) :
     365       18419 :         quorum(std::move(quorum)),
     366       18419 :         id(id),
     367       18419 :         msgHash(msgHash)
     368       18419 :     {
     369       36838 :     }
     370             : };
     371             : 
     372             : class CSigSharesManager : public llmq::CRecoveredSigsListener
     373             : {
     374             : private:
     375             :     static constexpr int64_t SESSION_NEW_SHARES_TIMEOUT{60};
     376             :     static constexpr int64_t SIG_SHARE_REQUEST_TIMEOUT{5};
     377             : 
     378             : public:
     379             :     // we try to keep total message size below 10k
     380             :     static constexpr size_t MAX_MSGS_CNT_QSIGSESANN{100};
     381             :     static constexpr size_t MAX_MSGS_CNT_QSIGSHARES{200};
     382             :     // 400 is the maximum quorum size, so this is also the maximum number of sigs we need to support
     383             :     static constexpr size_t MAX_MSGS_TOTAL_BATCHED_SIGS{400};
     384             :     static constexpr size_t MAX_MSGS_SIG_SHARES{32};
     385             : 
     386             : private:
     387             :     static constexpr int64_t EXP_SEND_FOR_RECOVERY_TIMEOUT{2000};
     388             :     static constexpr int64_t MAX_SEND_FOR_RECOVERY_TIMEOUT{10000};
     389             : 
     390             :     mutable Mutex cs;
     391             : 
     392             :     SigShareMap<CSigShare> sigShares GUARDED_BY(cs);
     393             :     Uint256HashMap<CSignedSession> signedSessions GUARDED_BY(cs);
     394             : 
     395             :     // stores time of last receivedSigShare. Used to detect timeouts
     396             :     Uint256HashMap<int64_t> timeSeenForSessions GUARDED_BY(cs);
     397             : 
     398             :     std::unordered_map<NodeId, CSigSharesNodeState> nodeStates GUARDED_BY(cs);
     399             :     SigShareMap<std::pair<NodeId, int64_t>> sigSharesRequested GUARDED_BY(cs);
     400             :     SigShareMap<bool> sigSharesQueuedToAnnounce GUARDED_BY(cs);
     401             : 
     402             :     Mutex cs_pendingSigns;
     403             :     std::vector<PendingSignatureData> pendingSigns GUARDED_BY(cs_pendingSigns);
     404             : 
     405             :     FastRandomContext rnd GUARDED_BY(cs);
     406             : 
     407             :     CConnman& m_connman;
     408             :     const ChainstateManager& m_chainman;
     409             :     CSigningManager& sigman;
     410             :     const CActiveMasternodeManager& m_mn_activeman;
     411             :     const CQuorumManager& qman;
     412             :     const CSporkManager& m_sporkman;
     413             : 
     414             :     CleanupThrottler<NodeClock> cleanupThrottler;
     415             :     std::atomic<uint32_t> recoveredSigsCounter{0};
     416             : 
     417             : public:
     418             :     CSigSharesManager() = delete;
     419             :     CSigSharesManager(const CSigSharesManager&) = delete;
     420             :     CSigSharesManager& operator=(const CSigSharesManager&) = delete;
     421             :     explicit CSigSharesManager(CConnman& connman, const ChainstateManager& chainman, CSigningManager& _sigman,
     422             :                                const CActiveMasternodeManager& mn_activeman, const CQuorumManager& _qman,
     423             :                                const CSporkManager& sporkman);
     424             :     ~CSigSharesManager() override;
     425             : 
     426             :     void RegisterRecoveryInterface() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     427             :     void UnregisterRecoveryInterface() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     428             : 
     429             :     void AsyncSign(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash)
     430             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns, !cs);
     431             :     std::optional<CSigShare> CreateSigShare(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
     432             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     433             :     void ForceReAnnouncement(const CQuorum& quorum, Consensus::LLMQType llmqType, const uint256& id,
     434             :                              const uint256& msgHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     435             : 
     436             :     [[nodiscard]] RecoveredSigResult HandleNewRecoveredSig(const CRecoveredSig& recoveredSig) override
     437             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     438             : 
     439             :     static CDeterministicMNCPtr SelectMemberForRecovery(const CQuorum& quorum, const uint256& id, int attempt);
     440             : 
     441             :     bool AsyncSignIfMember(Consensus::LLMQType llmqType, CSigningManager& sigman, const uint256& id,
     442             :                            const uint256& msgHash, const uint256& quorumHash = uint256(), bool allowReSign = false,
     443             :                            bool allowDiffMsgHashSigning = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns, !cs);
     444             : private:
     445             :     std::optional<CSigShare> CreateSigShareForSingleMember(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const;
     446             : 
     447             : public:
     448             :     // all of these return false when the currently processed message should be aborted (as each message actually contains multiple messages) and ban node
     449             :     bool ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     450             :     bool ProcessMessageSigShares(const CNode& pfrom, const CSigSharesInv& inv, const std::string& msg_type)
     451             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     452             :     bool ProcessMessageBatchedSigShares(const CNode& pfrom, const CBatchedSigShares& batchedSigShares)
     453             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     454             : 
     455             :     // if ProcessMessageSigShare returns false the node should be banned
     456             :     bool ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     457             : 
     458             :     // CollectPendingSigSharesToVerify returns true if there's more work to do
     459             :     bool CollectPendingSigSharesToVerify(
     460             :         size_t maxUniqueSessions, std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
     461             :         std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
     462             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     463             : 
     464             :     std::vector<std::shared_ptr<CRecoveredSig>> ProcessPendingSigShares(
     465             :         const std::vector<CSigShare>& sigSharesToProcess,
     466             :         const std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& quorums)
     467             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     468             : 
     469             : private:
     470             :     [[nodiscard]] std::shared_ptr<CRecoveredSig> ProcessSigShare(const CSigShare& sigShare, const CQuorumCPtr& quorum)
     471             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     472             :     [[nodiscard]] std::shared_ptr<CRecoveredSig> TryRecoverSig(const CQuorum& quorum, const uint256& id,
     473             :                                                                const uint256& msgHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     474             : 
     475             :     bool GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo)
     476             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     477             :     static CSigShare RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const std::pair<uint16_t, CBLSLazySignature>& in);
     478             : 
     479             :     void RemoveSigSharesForSession(const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs);
     480             : 
     481             : public:
     482             :     void RemoveNodesIf(std::function<bool(NodeId)> predicate) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     483             :     void MarkAsBanned(NodeId nodeId) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     484             : 
     485             : private:
     486             :     void CollectSigSharesToRequest(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToRequest)
     487             :         EXCLUSIVE_LOCKS_REQUIRED(cs);
     488             :     void CollectSigSharesToSend(std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>>& sigSharesToSend)
     489             :         EXCLUSIVE_LOCKS_REQUIRED(cs);
     490             :     void CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes) EXCLUSIVE_LOCKS_REQUIRED(cs);
     491             :     void CollectSigSharesToAnnounce(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToAnnounce)
     492             :         EXCLUSIVE_LOCKS_REQUIRED(cs);
     493             : 
     494             : public:
     495             :     void Cleanup() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     496             :     bool SendMessages() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     497             : 
     498             :     // Dispatcher functions
     499             :     std::vector<PendingSignatureData> DispatchPendingSigns() EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns);
     500             :     // Worker pool task functions
     501             :     bool IsAnyPendingProcessing() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     502             :     [[nodiscard]] std::shared_ptr<CRecoveredSig> SignAndProcessSingleShare(PendingSignatureData work)
     503             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns, !cs);
     504             : };
     505             : } // namespace llmq
     506             : 
     507             : #endif // BITCOIN_LLMQ_SIGNING_SHARES_H

Generated by: LCOV version 1.16