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 6023 : int64_t m_last_dsq{0}; 35 6023 : 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 6023 : int outboundAttemptCount{0}; 41 6023 : int64_t lastOutboundAttempt{0}; 42 6023 : int64_t lastOutboundSuccess{0}; 43 : 44 : //! bool flag is node currently under platform ban by p2p message 45 6023 : bool m_platform_ban{false}; 46 : //! height at which platform ban has been applied or removed 47 6023 : int m_platform_ban_updated{0}; 48 : 49 : public: 50 18928 : CMasternodeMetaInfo() = default; 51 3873 : explicit CMasternodeMetaInfo(const uint256& protx_hash) : 52 1291 : m_protx_hash(protx_hash) 53 1291 : { 54 2582 : } 55 23856 : CMasternodeMetaInfo(const CMasternodeMetaInfo& ref) = default; 56 : 57 10206 : SERIALIZE_METHODS(CMasternodeMetaInfo, obj) 58 : { 59 3402 : 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 3402 : } 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 3545 : void SetLastOutboundAttempt(int64_t t) { lastOutboundAttempt = t; ++outboundAttemptCount; } 71 2266 : void SetLastOutboundSuccess(int64_t t) { lastOutboundSuccess = t; outboundAttemptCount = 0; } 72 : 73 61 : bool SetPlatformBan(bool is_banned, int height) 74 : { 75 61 : if (height < m_platform_ban_updated) { 76 0 : return false; 77 : } 78 61 : if (height == m_platform_ban_updated && !is_banned) { 79 0 : return false; 80 : } 81 61 : m_platform_ban = is_banned; 82 61 : m_platform_ban_updated = height; 83 61 : return true; 84 61 : } 85 : }; 86 : 87 7314 : 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 7314 : 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 3752 : void Serialize(Stream &s) const EXCLUSIVE_LOCKS_REQUIRED(!cs) 104 : { 105 3752 : LOCK(cs); 106 3752 : std::vector<CMasternodeMetaInfo> tmpMetaInfo; 107 5730 : for (const auto& p : metaInfos) { 108 1978 : tmpMetaInfo.emplace_back(p.second); 109 : } 110 : // Convert deque to vector for serialization - unordered_set will be rebuilt on deserialization 111 3752 : std::vector<uint256> tmpUsedMasternodes(m_used_masternodes.begin(), m_used_masternodes.end()); 112 3752 : s << SERIALIZATION_VERSION_STRING << tmpMetaInfo << nDsqCount << tmpUsedMasternodes; 113 3752 : } 114 : 115 : template<typename Stream> 116 3452 : void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs) 117 : { 118 3452 : LOCK(cs); 119 : 120 3452 : metaInfos.clear(); 121 3452 : std::string strVersion; 122 3452 : s >> strVersion; 123 3452 : if (strVersion != SERIALIZATION_VERSION_STRING) { 124 0 : return; 125 : } 126 3452 : std::vector<CMasternodeMetaInfo> tmpMetaInfo; 127 3452 : std::vector<uint256> tmpUsedMasternodes; 128 3452 : s >> tmpMetaInfo >> nDsqCount >> tmpUsedMasternodes; 129 4876 : for (auto& mm : tmpMetaInfo) { 130 1424 : metaInfos.emplace(mm.m_protx_hash, CMasternodeMetaInfo{mm}); 131 : } 132 : 133 : // Convert vector to deque and build unordered_set for O(1) lookups 134 3452 : m_used_masternodes.assign(tmpUsedMasternodes.begin(), tmpUsedMasternodes.end()); 135 3452 : m_used_masternodes_set.clear(); 136 3452 : m_used_masternodes_set.insert(tmpUsedMasternodes.begin(), tmpUsedMasternodes.end()); 137 3452 : } 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 20 : int32_t m_requested_height{0}; 170 : uint256 m_quorum_hash; 171 : CBLSSignature m_signature; 172 : 173 60 : PlatformBanMessage() = default; 174 : 175 90 : SERIALIZE_METHODS(PlatformBanMessage, obj) 176 : { 177 30 : READWRITE(obj.m_protx_hash, obj.m_requested_height, obj.m_quorum_hash); 178 30 : if (!(s.GetType() & SER_GETHASH)) { 179 20 : READWRITE(CBLSSignatureVersionWrapper(const_cast<CBLSSignature&>(obj.m_signature), false)); 180 20 : } 181 30 : } 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 217816 : 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