LCOV - code coverage report
Current view: top level - src/masternode - meta.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 10 68 14.7 %
Date: 2026-06-25 07:23:51 Functions: 6 45 13.3 %

          Line data    Source code
       1             : // Copyright (c) 2014-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_MASTERNODE_META_H
       6             : #define BITCOIN_MASTERNODE_META_H
       7             : 
       8             : #include <bls/bls.h>
       9             : #include <saltedhasher.h>
      10             : #include <serialize.h>
      11             : #include <sync.h>
      12             : #include <threadsafety.h>
      13             : #include <uint256.h>
      14             : #include <unordered_lru_cache.h>
      15             : 
      16             : #include <deque>
      17             : #include <map>
      18             : #include <optional>
      19             : #include <vector>
      20             : 
      21             : class UniValue;
      22             : 
      23             : template<typename T>
      24             : class CFlatDB;
      25             : 
      26             : // Holds extra (non-deterministic) information about masternodes
      27             : // This is mostly local information, e.g. about mixing and governance
      28             : class CMasternodeMetaInfo
      29             : {
      30             : public:
      31             :     uint256 m_protx_hash;
      32             : 
      33             :     //! the dsq count from the last dsq broadcast of this node
      34         146 :     int64_t m_last_dsq{0};
      35         146 :     int m_mixing_tx_count{0};
      36             : 
      37             :     // KEEP TRACK OF GOVERNANCE ITEMS EACH MASTERNODE HAS VOTE UPON FOR RECALCULATION
      38             :     std::map<uint256, int> mapGovernanceObjectsVotedOn;
      39             : 
      40         146 :     int outboundAttemptCount{0};
      41         146 :     int64_t lastOutboundAttempt{0};
      42         146 :     int64_t lastOutboundSuccess{0};
      43             : 
      44             :     //! bool flag is node currently under platform ban by p2p message
      45         146 :     bool m_platform_ban{false};
      46             :     //! height at which platform ban has been applied or removed
      47         146 :     int m_platform_ban_updated{0};
      48             : 
      49             : public:
      50         584 :     CMasternodeMetaInfo() = default;
      51           0 :     explicit CMasternodeMetaInfo(const uint256& protx_hash) :
      52           0 :         m_protx_hash(protx_hash)
      53           0 :     {
      54           0 :     }
      55           0 :     CMasternodeMetaInfo(const CMasternodeMetaInfo& ref) = default;
      56             : 
      57           0 :     SERIALIZE_METHODS(CMasternodeMetaInfo, obj)
      58             :     {
      59           0 :         READWRITE(obj.m_protx_hash, obj.m_last_dsq, obj.m_mixing_tx_count, obj.mapGovernanceObjectsVotedOn,
      60             :                   obj.outboundAttemptCount, obj.lastOutboundAttempt, obj.lastOutboundSuccess, obj.m_platform_ban,
      61             :                   obj.m_platform_ban_updated);
      62           0 :     }
      63             : 
      64             :     UniValue ToJson() const;
      65             : 
      66             :     // KEEP TRACK OF EACH GOVERNANCE ITEM IN CASE THIS NODE GOES OFFLINE, SO WE CAN RECALCULATE THEIR STATUS
      67             :     void AddGovernanceVote(const uint256& nGovernanceObjectHash);
      68             :     void RemoveGovernanceObject(const uint256& nGovernanceObjectHash);
      69             : 
      70           0 :     void SetLastOutboundAttempt(int64_t t) { lastOutboundAttempt = t; ++outboundAttemptCount; }
      71           0 :     void SetLastOutboundSuccess(int64_t t) { lastOutboundSuccess = t; outboundAttemptCount = 0; }
      72             : 
      73           0 :     bool SetPlatformBan(bool is_banned, int height)
      74             :     {
      75           0 :         if (height < m_platform_ban_updated) {
      76           0 :             return false;
      77             :         }
      78           0 :         if (height == m_platform_ban_updated && !is_banned) {
      79           0 :             return false;
      80             :         }
      81           0 :         m_platform_ban = is_banned;
      82           0 :         m_platform_ban_updated = height;
      83           0 :         return true;
      84           0 :     }
      85             : };
      86             : 
      87         627 : class MasternodeMetaStore
      88             : {
      89             : protected:
      90             :     static const std::string SERIALIZATION_VERSION_STRING;
      91             : 
      92             :     mutable Mutex cs;
      93             :     std::map<uint256, CMasternodeMetaInfo> metaInfos GUARDED_BY(cs);
      94             :     // keep track of dsq count to prevent masternodes from gaming coinjoin queue
      95         627 :     int64_t nDsqCount GUARDED_BY(cs){0};
      96             :     // keep track of the used Masternodes for CoinJoin across all wallets
      97             :     // Using deque for efficient FIFO removal and unordered_set for O(1) lookups
      98             :     std::deque<uint256> m_used_masternodes GUARDED_BY(cs);
      99             :     Uint256HashSet m_used_masternodes_set GUARDED_BY(cs);
     100             : 
     101             : public:
     102             :     template<typename Stream>
     103           0 :     void Serialize(Stream &s) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
     104             :     {
     105           0 :         LOCK(cs);
     106           0 :         std::vector<CMasternodeMetaInfo> tmpMetaInfo;
     107           0 :         for (const auto& p : metaInfos) {
     108           0 :             tmpMetaInfo.emplace_back(p.second);
     109             :         }
     110             :         // Convert deque to vector for serialization - unordered_set will be rebuilt on deserialization
     111           0 :         std::vector<uint256> tmpUsedMasternodes(m_used_masternodes.begin(), m_used_masternodes.end());
     112           0 :         s << SERIALIZATION_VERSION_STRING << tmpMetaInfo << nDsqCount << tmpUsedMasternodes;
     113           0 :     }
     114             : 
     115             :     template<typename Stream>
     116           0 :     void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs)
     117             :     {
     118           0 :         LOCK(cs);
     119             : 
     120           0 :         metaInfos.clear();
     121           0 :         std::string strVersion;
     122           0 :         s >> strVersion;
     123           0 :         if (strVersion != SERIALIZATION_VERSION_STRING) {
     124           0 :             return;
     125             :         }
     126           0 :         std::vector<CMasternodeMetaInfo> tmpMetaInfo;
     127           0 :         std::vector<uint256> tmpUsedMasternodes;
     128           0 :         s >> tmpMetaInfo >> nDsqCount >> tmpUsedMasternodes;
     129           0 :         for (auto& mm : tmpMetaInfo) {
     130           0 :             metaInfos.emplace(mm.m_protx_hash, CMasternodeMetaInfo{mm});
     131             :         }
     132             : 
     133             :         // Convert vector to deque and build unordered_set for O(1) lookups
     134           0 :         m_used_masternodes.assign(tmpUsedMasternodes.begin(), tmpUsedMasternodes.end());
     135           0 :         m_used_masternodes_set.clear();
     136           0 :         m_used_masternodes_set.insert(tmpUsedMasternodes.begin(), tmpUsedMasternodes.end());
     137           0 :     }
     138             : 
     139           0 :     void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs)
     140             :     {
     141           0 :         LOCK(cs);
     142             : 
     143           0 :         metaInfos.clear();
     144           0 :         m_used_masternodes.clear();
     145           0 :         m_used_masternodes_set.clear();
     146           0 :     }
     147             : 
     148             :     std::string ToString() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     149             : };
     150             : 
     151             : /**
     152             :  * Platform PoSe Ban are result in the node voting against the targeted evonode in all future DKG sessions until that targeted
     153             :  *evonode has been successfully banned. Platform will initiate this ban process by passing relevant information to Core using RPC. See DIP-0031
     154             :  *
     155             :  * We use 2 main classes to manage Platform PoSe Ban
     156             :  *
     157             :  * PlatformBanMessage
     158             :  * CMasternodeMetaInfo - a higher-level construct which store extra (non-deterministic) information about masternodes including platform ban status
     159             :  **/
     160             : 
     161             : /**
     162             :  * PlatformBanMessage - low-level constructs which present p2p message PlatformBan containing the protx hash, requested
     163             :  * height to ban, and BLS data: quorum hash and bls signature
     164             :  */
     165             : class PlatformBanMessage
     166             : {
     167             : public:
     168             :     uint256 m_protx_hash;
     169           0 :     int32_t m_requested_height{0};
     170             :     uint256 m_quorum_hash;
     171             :     CBLSSignature m_signature;
     172             : 
     173           0 :     PlatformBanMessage() = default;
     174             : 
     175           0 :     SERIALIZE_METHODS(PlatformBanMessage, obj)
     176             :     {
     177           0 :         READWRITE(obj.m_protx_hash, obj.m_requested_height, obj.m_quorum_hash);
     178           0 :         if (!(s.GetType() & SER_GETHASH)) {
     179           0 :             READWRITE(CBLSSignatureVersionWrapper(const_cast<CBLSSignature&>(obj.m_signature), false));
     180           0 :         }
     181           0 :     }
     182             : 
     183             :     uint256 GetHash() const;
     184             : };
     185             : 
     186             : class CMasternodeMetaMan : public MasternodeMetaStore
     187             : {
     188             : private:
     189             :     using db_type = CFlatDB<MasternodeMetaStore>;
     190             : 
     191             : private:
     192             :     const std::unique_ptr<db_type> m_db;
     193             :     bool is_valid{false};
     194             : 
     195             :     std::vector<uint256> vecDirtyGovernanceObjectHashes GUARDED_BY(cs);
     196             : 
     197             :     // equal to double of expected amount of all evo nodes, see DIP-0028
     198             :     // it consumes no more than 1Mb of RAM but will cover extreme cases
     199             :     static constexpr size_t SeenBanInventorySize = 900;
     200             :     mutable unordered_lru_cache<uint256, PlatformBanMessage, StaticSaltedHasher> m_seen_platform_bans GUARDED_BY(cs){
     201             :         SeenBanInventorySize};
     202             : 
     203             :     CMasternodeMetaInfo& GetMetaInfo(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs);
     204             :     const CMasternodeMetaInfo& GetMetaInfoOrDefault(const uint256& proTxHash) const EXCLUSIVE_LOCKS_REQUIRED(cs);
     205             : 
     206             : public:
     207             :     CMasternodeMetaMan(const CMasternodeMetaMan&) = delete;
     208             :     CMasternodeMetaMan& operator=(const CMasternodeMetaMan&) = delete;
     209             :     CMasternodeMetaMan();
     210             :     ~CMasternodeMetaMan();
     211             : 
     212             :     bool LoadCache(bool load_cache);
     213             : 
     214           0 :     bool IsValid() const { return is_valid; }
     215             : 
     216             :     CMasternodeMetaInfo GetInfo(const uint256& proTxHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     217             : 
     218             :     // We keep track of dsq (mixing queues) count to avoid using same masternodes for mixing too often.
     219             :     // MN's threshold is calculated as the last dsq count this specific masternode was used in a mixing
     220             :     // session plus a margin of 20% of masternode count. In other words we expect at least 20% of unique
     221             :     // masternodes before we ever see a masternode that we know already mixed someone's funds earlier.
     222             :     bool IsMixingThresholdExceeded(const uint256& protx_hash, int mn_count) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     223             : 
     224             :     void AllowMixing(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     225             :     void DisallowMixing(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     226             :     bool IsValidForMixingTxes(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     227             : 
     228             :     void AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash);
     229             :     void RemoveGovernanceObject(const uint256& nGovernanceObjectHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     230             : 
     231             :     std::vector<uint256> GetAndClearDirtyGovernanceObjectHashes() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     232             : 
     233             :     void SetLastOutboundAttempt(const uint256& protx_hash, int64_t t) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     234             :     void SetLastOutboundSuccess(const uint256& protx_hash, int64_t t) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     235             :     int64_t GetLastOutboundAttempt(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     236             :     int64_t GetLastOutboundSuccess(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     237             :     bool OutboundFailedTooManyTimes(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     238             : 
     239             :     bool IsPlatformBanned(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     240             :     bool ResetPlatformBan(const uint256& protx_hash, int height) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     241             :     bool SetPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     242             :     bool AlreadyHavePlatformBan(const uint256& inv_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     243             :     std::optional<PlatformBanMessage> GetPlatformBan(const uint256& inv_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     244             : 
     245             :     // CoinJoin masternode tracking
     246             :     void AddUsedMasternode(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     247             :     void RemoveUsedMasternodes(size_t count) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     248             :     size_t GetUsedMasternodesCount() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     249             :     bool IsUsedMasternode(const uint256& proTxHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     250             : };
     251             : 
     252             : #endif // BITCOIN_MASTERNODE_META_H

Generated by: LCOV version 1.16