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 : #include <masternode/meta.h> 6 : 7 : #include <flat-database.h> 8 : #include <univalue.h> 9 : #include <util/time.h> 10 : 11 146 : const std::string MasternodeMetaStore::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-5"; 12 : 13 : static constexpr int MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS{5}; 14 : static constexpr int MASTERNODE_MAX_MIXING_TXES{5}; 15 : 16 : namespace { 17 146 : static const CMasternodeMetaInfo default_meta_info{}; 18 : } // anonymous namespace 19 : 20 1254 : CMasternodeMetaMan::CMasternodeMetaMan() : 21 627 : m_db{std::make_unique<db_type>("mncache.dat", "magicMasternodeCache")} 22 627 : { 23 627 : } 24 : 25 1252 : CMasternodeMetaMan::~CMasternodeMetaMan() 26 626 : { 27 626 : if (!is_valid) return; 28 0 : m_db->Store(*this); 29 1252 : } 30 : 31 0 : bool CMasternodeMetaMan::LoadCache(bool load_cache) 32 : { 33 0 : assert(m_db != nullptr); 34 0 : is_valid = load_cache ? m_db->Load(*this) : m_db->Store(*this); 35 0 : return is_valid; 36 : } 37 : 38 0 : UniValue CMasternodeMetaInfo::ToJson() const 39 : { 40 0 : int64_t now = GetTime<std::chrono::seconds>().count(); 41 : 42 0 : UniValue ret(UniValue::VOBJ); 43 0 : ret.pushKV("lastDSQ", m_last_dsq); 44 0 : ret.pushKV("mixingTxCount", m_mixing_tx_count); 45 0 : ret.pushKV("outboundAttemptCount", outboundAttemptCount); 46 0 : ret.pushKV("lastOutboundAttempt", lastOutboundAttempt); 47 0 : ret.pushKV("lastOutboundAttemptElapsed", now - lastOutboundAttempt); 48 0 : ret.pushKV("lastOutboundSuccess", lastOutboundSuccess); 49 0 : ret.pushKV("lastOutboundSuccessElapsed", now - lastOutboundSuccess); 50 0 : ret.pushKV("is_platform_banned", m_platform_ban); 51 0 : ret.pushKV("platform_ban_height_updated", m_platform_ban_updated); 52 : 53 0 : return ret; 54 0 : } 55 : 56 0 : void CMasternodeMetaInfo::AddGovernanceVote(const uint256& nGovernanceObjectHash) 57 : { 58 : // Insert a zero value, or not. Then increment the value regardless. This 59 : // ensures the value is in the map. 60 0 : const auto& pair = mapGovernanceObjectsVotedOn.emplace(nGovernanceObjectHash, 0); 61 0 : pair.first->second++; 62 0 : } 63 : 64 0 : void CMasternodeMetaInfo::RemoveGovernanceObject(const uint256& nGovernanceObjectHash) 65 : { 66 : // Whether or not the govobj hash exists in the map first is irrelevant. 67 0 : mapGovernanceObjectsVotedOn.erase(nGovernanceObjectHash); 68 0 : } 69 : 70 0 : const CMasternodeMetaInfo& CMasternodeMetaMan::GetMetaInfoOrDefault(const uint256& protx_hash) const 71 : { 72 0 : const auto it = metaInfos.find(protx_hash); 73 0 : if (it == metaInfos.end()) return default_meta_info; 74 0 : return it->second; 75 0 : } 76 : 77 0 : CMasternodeMetaInfo CMasternodeMetaMan::GetInfo(const uint256& proTxHash) const 78 : { 79 0 : LOCK(cs); 80 0 : return GetMetaInfoOrDefault(proTxHash); 81 0 : } 82 : 83 0 : CMasternodeMetaInfo& CMasternodeMetaMan::GetMetaInfo(const uint256& proTxHash) 84 : { 85 0 : auto it = metaInfos.find(proTxHash); 86 0 : if (it != metaInfos.end()) { 87 0 : return it->second; 88 : } 89 0 : it = metaInfos.emplace(proTxHash, CMasternodeMetaInfo{proTxHash}).first; 90 0 : return it->second; 91 0 : } 92 : 93 0 : bool CMasternodeMetaMan::IsMixingThresholdExceeded(const uint256& protx_hash, int mn_count) const 94 : { 95 0 : LOCK(cs); 96 0 : auto it = metaInfos.find(protx_hash); 97 0 : if (it == metaInfos.end()) { 98 0 : LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString()); 99 0 : return false; 100 : } 101 0 : const auto& meta_info = it->second; 102 0 : int64_t last_dsq = meta_info.m_last_dsq; 103 0 : int64_t threshold = last_dsq + mn_count / 5; 104 : 105 0 : LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n", 106 : protx_hash.ToString(), last_dsq, threshold, nDsqCount); 107 0 : return last_dsq != 0 && threshold > nDsqCount; 108 0 : } 109 : 110 0 : void CMasternodeMetaMan::AllowMixing(const uint256& proTxHash) 111 : { 112 0 : LOCK(cs); 113 0 : auto& mm = GetMetaInfo(proTxHash); 114 0 : mm.m_last_dsq = ++nDsqCount; 115 0 : mm.m_mixing_tx_count = 0; 116 0 : } 117 : 118 0 : void CMasternodeMetaMan::DisallowMixing(const uint256& proTxHash) 119 : { 120 0 : LOCK(cs); 121 0 : GetMetaInfo(proTxHash).m_mixing_tx_count++; 122 0 : } 123 : 124 0 : bool CMasternodeMetaMan::IsValidForMixingTxes(const uint256& protx_hash) const 125 : { 126 0 : LOCK(cs); 127 0 : return GetMetaInfoOrDefault(protx_hash).m_mixing_tx_count <= MASTERNODE_MAX_MIXING_TXES; 128 0 : } 129 : 130 0 : void CMasternodeMetaMan::AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash) 131 : { 132 0 : LOCK(cs); 133 0 : GetMetaInfo(proTxHash).AddGovernanceVote(nGovernanceObjectHash); 134 0 : } 135 : 136 0 : void CMasternodeMetaMan::RemoveGovernanceObject(const uint256& nGovernanceObjectHash) 137 : { 138 0 : LOCK(cs); 139 0 : for (auto& [_, meta_info] : metaInfos) { 140 0 : meta_info.RemoveGovernanceObject(nGovernanceObjectHash); 141 : } 142 0 : } 143 : 144 0 : std::vector<uint256> CMasternodeMetaMan::GetAndClearDirtyGovernanceObjectHashes() 145 : { 146 0 : std::vector<uint256> vecTmp; 147 0 : WITH_LOCK(cs, vecTmp.swap(vecDirtyGovernanceObjectHashes)); 148 0 : return vecTmp; 149 0 : } 150 : 151 0 : void CMasternodeMetaMan::SetLastOutboundAttempt(const uint256& protx_hash, int64_t t) 152 : { 153 0 : LOCK(cs); 154 0 : GetMetaInfo(protx_hash).SetLastOutboundAttempt(t); 155 0 : } 156 : 157 0 : void CMasternodeMetaMan::SetLastOutboundSuccess(const uint256& protx_hash, int64_t t) 158 : { 159 0 : LOCK(cs); 160 0 : GetMetaInfo(protx_hash).SetLastOutboundSuccess(t); 161 0 : } 162 : 163 0 : int64_t CMasternodeMetaMan::GetLastOutboundAttempt(const uint256& protx_hash) const 164 : { 165 0 : LOCK(cs); 166 0 : return GetMetaInfoOrDefault(protx_hash).lastOutboundAttempt; 167 0 : } 168 : 169 0 : int64_t CMasternodeMetaMan::GetLastOutboundSuccess(const uint256& protx_hash) const 170 : { 171 0 : LOCK(cs); 172 0 : return GetMetaInfoOrDefault(protx_hash).lastOutboundSuccess; 173 0 : } 174 : 175 0 : bool CMasternodeMetaMan::OutboundFailedTooManyTimes(const uint256& protx_hash) const 176 : { 177 0 : LOCK(cs); 178 0 : return GetMetaInfoOrDefault(protx_hash).outboundAttemptCount > MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS; 179 0 : } 180 : 181 0 : bool CMasternodeMetaMan::IsPlatformBanned(const uint256& protx_hash) const 182 : { 183 0 : LOCK(cs); 184 0 : return GetMetaInfoOrDefault(protx_hash).m_platform_ban; 185 0 : } 186 : 187 4 : bool CMasternodeMetaMan::ResetPlatformBan(const uint256& protx_hash, int height) 188 : { 189 4 : LOCK(cs); 190 : 191 4 : auto it = metaInfos.find(protx_hash); 192 4 : if (it == metaInfos.end()) return false; 193 : 194 0 : return it->second.SetPlatformBan(false, height); 195 4 : } 196 : 197 0 : bool CMasternodeMetaMan::SetPlatformBan(const uint256& inv_hash, PlatformBanMessage&& ban_msg) 198 : { 199 0 : LOCK(cs); 200 : 201 0 : const uint256& protx_hash = ban_msg.m_protx_hash; 202 : 203 0 : bool ret = GetMetaInfo(protx_hash).SetPlatformBan(true, ban_msg.m_requested_height); 204 0 : if (ret) { 205 0 : m_seen_platform_bans.emplace(inv_hash, std::move(ban_msg)); 206 0 : } 207 0 : return ret; 208 0 : } 209 : 210 0 : bool CMasternodeMetaMan::AlreadyHavePlatformBan(const uint256& inv_hash) const 211 : { 212 0 : LOCK(cs); 213 0 : return m_seen_platform_bans.exists(inv_hash); 214 0 : } 215 : 216 0 : std::optional<PlatformBanMessage> CMasternodeMetaMan::GetPlatformBan(const uint256& inv_hash) const 217 : { 218 0 : LOCK(cs); 219 0 : PlatformBanMessage ret; 220 0 : if (!m_seen_platform_bans.get(inv_hash, ret)) { 221 0 : return std::nullopt; 222 : } 223 : 224 0 : return ret; 225 0 : } 226 : 227 0 : void CMasternodeMetaMan::AddUsedMasternode(const uint256& proTxHash) 228 : { 229 0 : LOCK(cs); 230 : // Only add if not already present (prevents duplicates) 231 0 : if (m_used_masternodes_set.insert(proTxHash).second) { 232 0 : m_used_masternodes.push_back(proTxHash); 233 0 : } 234 0 : } 235 : 236 0 : void CMasternodeMetaMan::RemoveUsedMasternodes(size_t count) 237 : { 238 0 : LOCK(cs); 239 0 : size_t removed = 0; 240 0 : while (removed < count && !m_used_masternodes.empty()) { 241 : // Remove from both the set and the deque 242 0 : m_used_masternodes_set.erase(m_used_masternodes.front()); 243 0 : m_used_masternodes.pop_front(); 244 0 : ++removed; 245 : } 246 0 : } 247 : 248 0 : size_t CMasternodeMetaMan::GetUsedMasternodesCount() const 249 : { 250 0 : LOCK(cs); 251 0 : return m_used_masternodes.size(); 252 0 : } 253 : 254 0 : bool CMasternodeMetaMan::IsUsedMasternode(const uint256& proTxHash) const 255 : { 256 0 : LOCK(cs); 257 0 : return m_used_masternodes_set.find(proTxHash) != m_used_masternodes_set.end(); 258 0 : } 259 : 260 0 : std::string MasternodeMetaStore::ToString() const 261 : { 262 0 : LOCK(cs); 263 0 : return strprintf("Masternodes: meta infos object count: %d, nDsqCount: %d, used masternodes count: %d", 264 0 : metaInfos.size(), nDsqCount, m_used_masternodes.size()); 265 0 : } 266 : 267 0 : uint256 PlatformBanMessage::GetHash() const { return ::SerializeHash(*this); }