LCOV - code coverage report
Current view: top level - src/llmq - signing.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 0 49 0.0 %
Date: 2026-06-25 07:23:51 Functions: 0 34 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             : #ifndef BITCOIN_LLMQ_SIGNING_H
       6             : #define BITCOIN_LLMQ_SIGNING_H
       7             : 
       8             : #include <bls/bls.h>
       9             : #include <llmq/params.h>
      10             : #include <llmq/types.h>
      11             : #include <net_types.h>
      12             : #include <random.h>
      13             : #include <saltedhasher.h>
      14             : #include <sync.h>
      15             : #include <unordered_lru_cache.h>
      16             : 
      17             : #include <memory>
      18             : #include <string_view>
      19             : #include <unordered_map>
      20             : #include <variant>
      21             : 
      22             : class CChainState;
      23             : class CDataStream;
      24             : class CDBBatch;
      25             : class CDBWrapper;
      26             : class CInv;
      27             : class CTransaction;
      28             : struct RPCResult;
      29             : namespace util {
      30             : struct DbWrapperParams;
      31             : } // namespace util
      32             : 
      33             : class UniValue;
      34             : using CTransactionRef = std::shared_ptr<const CTransaction>;
      35             : 
      36             : namespace llmq {
      37             : 
      38             : using RecoveredSigResult = std::variant<std::monostate, CInv, CTransactionRef>;
      39             : 
      40             : class CQuorumManager;
      41             : class CSigSharesManager;
      42             : class SignHash;
      43             : 
      44             : // Keep recovered signatures for a week. This is a "-maxrecsigsage" option default.
      45             : static constexpr int64_t DEFAULT_MAX_RECOVERED_SIGS_AGE{60 * 60 * 24 * 7};
      46             : 
      47             : class CSigBase
      48             : {
      49             : protected:
      50           0 :     Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
      51             :     uint256 quorumHash;
      52             :     uint256 id;
      53             :     uint256 msgHash;
      54             : 
      55           0 :     CSigBase(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash)
      56           0 :             : llmqType(llmqType), quorumHash(quorumHash), id(id), msgHash(msgHash) {};
      57           0 :     CSigBase() = default;
      58             : 
      59             : public:
      60           0 :     [[nodiscard]] constexpr Consensus::LLMQType getLlmqType() const { return llmqType; }
      61             : 
      62           0 :     [[nodiscard]] constexpr auto getQuorumHash() const -> const uint256& {
      63           0 :         return quorumHash;
      64             :     }
      65             : 
      66           0 :     [[nodiscard]] constexpr auto getId() const -> const uint256& {
      67           0 :         return id;
      68             :     }
      69             : 
      70           0 :     [[nodiscard]] constexpr auto getMsgHash() const -> const uint256& {
      71           0 :         return msgHash;
      72             :     }
      73             : 
      74             :     [[nodiscard]] SignHash buildSignHash() const;
      75             : };
      76             : 
      77             : class CRecoveredSig : virtual public CSigBase
      78             : {
      79             : public:
      80             :     const CBLSLazySignature sig;
      81             : 
      82           0 :     CRecoveredSig() = default;
      83             : 
      84           0 :     CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSLazySignature& _sig) :
      85           0 :                   CSigBase(_llmqType, _quorumHash, _id, _msgHash), sig(_sig) {UpdateHash();};
      86           0 :     CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSSignature& _sig) :
      87           0 :                   CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast<CBLSLazySignature&>(sig).Set(_sig, bls::bls_legacy_scheme.load()); UpdateHash();};
      88             : 
      89             : private:
      90             :     // only in-memory
      91             :     uint256 hash;
      92             : 
      93           0 :     void UpdateHash()
      94             :     {
      95           0 :         hash = ::SerializeHash(*this);
      96           0 :     }
      97             : 
      98             : public:
      99           0 :     SERIALIZE_METHODS(CRecoveredSig, obj)
     100             :     {
     101           0 :         READWRITE(const_cast<Consensus::LLMQType&>(obj.llmqType), const_cast<uint256&>(obj.quorumHash), const_cast<uint256&>(obj.id),
     102             :                   const_cast<uint256&>(obj.msgHash), const_cast<CBLSLazySignature&>(obj.sig));
     103           0 :         SER_READ(obj, obj.UpdateHash());
     104           0 :     }
     105             : 
     106           0 :     const uint256& GetHash() const
     107             :     {
     108           0 :         assert(!hash.IsNull());
     109           0 :         return hash;
     110             :     }
     111             : 
     112             :     [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
     113             :     [[nodiscard]] UniValue ToJson() const;
     114             : };
     115             : 
     116             : class CRecoveredSigsDb
     117             : {
     118             : private:
     119             :     std::unique_ptr<CDBWrapper> db{nullptr};
     120             : 
     121             :     mutable Mutex cs_cache;
     122             :     mutable unordered_lru_cache<std::pair<Consensus::LLMQType, uint256>, bool, StaticSaltedHasher, 30000> hasSigForIdCache GUARDED_BY(cs_cache);
     123             :     mutable Uint256LruHashMap<bool, 30000> hasSigForSessionCache GUARDED_BY(cs_cache);
     124             :     mutable Uint256LruHashMap<bool, 30000> hasSigForHashCache GUARDED_BY(cs_cache);
     125             : 
     126             : public:
     127             :     explicit CRecoveredSigsDb(const util::DbWrapperParams& db_params);
     128             :     ~CRecoveredSigsDb();
     129             : 
     130             :     bool HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash) const;
     131             :     bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id) const EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     132             :     bool HasRecoveredSigForSession(const uint256& signHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     133             :     bool HasRecoveredSigForHash(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     134             :     bool GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret) const;
     135             :     bool GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret) const;
     136             :     void WriteRecoveredSig(const CRecoveredSig& recSig) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     137             :     void TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     138             : 
     139             :     void CleanupOldRecoveredSigs(int64_t maxAge) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     140             : 
     141             :     // votes are removed when the recovered sig is written to the db
     142             :     bool HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id) const;
     143             :     bool GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet) const;
     144             :     void WriteVoteForId(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
     145             : 
     146             :     void CleanupOldVotes(int64_t maxAge);
     147             : 
     148             : private:
     149             :     bool ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret) const;
     150             :     void RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteHashKey,
     151             :                             bool deleteTimeKey) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     152             : };
     153             : 
     154             : class CRecoveredSigsListener
     155             : {
     156             : public:
     157           0 :     virtual ~CRecoveredSigsListener() = default;
     158             : 
     159             :     [[nodiscard]] virtual RecoveredSigResult HandleNewRecoveredSig(const CRecoveredSig& recoveredSig) = 0;
     160             : };
     161             : 
     162             : class CSigningManager
     163             : {
     164             : private:
     165             :     CRecoveredSigsDb db;
     166             :     const CQuorumManager& qman;
     167             :     const int64_t m_max_recsigs_age;
     168             : 
     169             :     mutable Mutex cs_pending;
     170             :     // Incoming and not verified yet
     171             :     std::unordered_map<NodeId, std::list<std::shared_ptr<const CRecoveredSig>>> pendingRecoveredSigs GUARDED_BY(cs_pending);
     172             :     Uint256HashMap<std::shared_ptr<const CRecoveredSig>> pendingReconstructedRecoveredSigs GUARDED_BY(cs_pending);
     173             : 
     174             :     FastRandomContext rnd GUARDED_BY(cs_pending);
     175             : 
     176             :     mutable Mutex cs_listeners;
     177             :     std::vector<CRecoveredSigsListener*> recoveredSigsListeners GUARDED_BY(cs_listeners);
     178             : 
     179             : public:
     180             :     CSigningManager() = delete;
     181             :     CSigningManager(const CSigningManager&) = delete;
     182             :     CSigningManager& operator=(const CSigningManager&) = delete;
     183             :     explicit CSigningManager(const CQuorumManager& _qman, const util::DbWrapperParams& db_params, int64_t max_recsigs_age);
     184             :     ~CSigningManager();
     185             : 
     186             :     bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);
     187             :     bool GetRecoveredSigForGetData(const uint256& hash, CRecoveredSig& ret) const;
     188             : 
     189             :     void VerifyAndProcessRecoveredSig(NodeId from, std::shared_ptr<CRecoveredSig> recovered_sig)
     190             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);
     191             : 
     192             :     // This is called when a recovered signature was was reconstructed from another P2P message and is known to be valid
     193             :     // This is the case for example when a signature appears as part of InstantSend or ChainLocks
     194             :     void PushReconstructedRecoveredSig(const std::shared_ptr<const CRecoveredSig>& recoveredSig)
     195             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);
     196             : 
     197             :     // This is called when a recovered signature can be safely removed from the DB. This is only safe when some other
     198             :     // mechanism prevents possible conflicts. As an example, ChainLocks prevent conflicts in confirmed TXs InstantSend votes
     199             :     // This won't completely remove all traces of the recovered sig but instead leave the hash and signHash entries in the
     200             :     // DB. This allows AlreadyHave/late-share filtering to keep returning true. Cleanup will later remove the remains
     201             :     void TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);
     202             : 
     203             :     // Used by NetSigning:
     204             :     [[nodiscard]] Uint256HashMap<std::shared_ptr<const CRecoveredSig>> FetchPendingReconstructed()
     205             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);
     206             :     [[nodiscard]] bool CollectPendingRecoveredSigsToVerify(
     207             :         size_t maxUniqueSessions, std::unordered_map<NodeId, std::list<std::shared_ptr<const CRecoveredSig>>>& retSigShares,
     208             :         std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CBLSPublicKey, StaticSaltedHasher>& ret_pubkeys)
     209             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);
     210             :     [[nodiscard]] std::vector<CRecoveredSigsListener*> GetListeners() const EXCLUSIVE_LOCKS_REQUIRED(!cs_listeners);
     211             :     // Returns true if recovered sigs should be send to listeners
     212             :     [[nodiscard]] bool ProcessRecoveredSig(const std::shared_ptr<const CRecoveredSig>& recoveredSig)
     213             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);
     214             : 
     215             : private:
     216             :     // Used by CSigSharesManager
     217           0 :     CRecoveredSigsDb& GetDb() { return db; }
     218             : 
     219             :     // Needed for access to GetDb() and ProcessRecoveredSig()
     220             :     friend class CSigSharesManager;
     221             : 
     222             : public:
     223             :     // public interface
     224             :     void RegisterRecoveredSigsListener(CRecoveredSigsListener* l) EXCLUSIVE_LOCKS_REQUIRED(!cs_listeners);
     225             :     void UnregisterRecoveredSigsListener(CRecoveredSigsListener* l) EXCLUSIVE_LOCKS_REQUIRED(!cs_listeners);
     226             : 
     227             :     bool HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash) const;
     228             :     bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id) const;
     229             :     bool HasRecoveredSigForSession(const uint256& signHash) const;
     230             :     bool GetRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& retRecSig) const;
     231             :     bool IsConflicting(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash) const;
     232             : 
     233             :     bool GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet) const;
     234             : 
     235             : public:
     236             :     void Cleanup();
     237             : };
     238             : 
     239             : template<typename NodesContainer, typename Continue, typename Callback>
     240           0 : void IterateNodesRandom(NodesContainer& nodeStates, Continue&& cont, Callback&& callback, FastRandomContext& rnd)
     241             : {
     242           0 :     std::vector<typename NodesContainer::iterator> rndNodes;
     243           0 :     rndNodes.reserve(nodeStates.size());
     244           0 :     for (auto it = nodeStates.begin(); it != nodeStates.end(); ++it) {
     245           0 :         rndNodes.emplace_back(it);
     246           0 :     }
     247           0 :     if (rndNodes.empty()) {
     248           0 :         return;
     249             :     }
     250           0 :     Shuffle(rndNodes.begin(), rndNodes.end(), rnd);
     251             : 
     252           0 :     size_t idx = 0;
     253           0 :     while (!rndNodes.empty() && cont()) {
     254           0 :         auto nodeId = rndNodes[idx]->first;
     255           0 :         auto& ns = rndNodes[idx]->second;
     256             : 
     257           0 :         if (callback(nodeId, ns)) {
     258           0 :             idx = (idx + 1) % rndNodes.size();
     259           0 :         } else {
     260           0 :             rndNodes.erase(rndNodes.begin() + idx);
     261           0 :             if (rndNodes.empty()) {
     262           0 :                 break;
     263             :             }
     264           0 :             idx %= rndNodes.size();
     265             :         }
     266             :     }
     267           0 : }
     268             : 
     269             : bool IsQuorumActive(Consensus::LLMQType llmqType, const CQuorumManager& qman, const uint256& quorumHash);
     270             : 
     271             : } // namespace llmq
     272             : 
     273             : #endif // BITCOIN_LLMQ_SIGNING_H

Generated by: LCOV version 1.16