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 3308 : 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 3308 : static const CMasternodeMetaInfo default_meta_info{}; 18 : } // anonymous namespace 19 : 20 7124 : CMasternodeMetaMan::CMasternodeMetaMan() : 21 3562 : m_db{std::make_unique<db_type>("mncache.dat", "magicMasternodeCache")} 22 3562 : { 23 3562 : } 24 : 25 7122 : CMasternodeMetaMan::~CMasternodeMetaMan() 26 3561 : { 27 3561 : if (!is_valid) return; 28 2857 : m_db->Store(*this); 29 7122 : } 30 : 31 2857 : bool CMasternodeMetaMan::LoadCache(bool load_cache) 32 : { 33 2857 : assert(m_db != nullptr); 34 2857 : is_valid = load_cache ? m_db->Load(*this) : m_db->Store(*this); 35 2857 : return is_valid; 36 : } 37 : 38 3758 : UniValue CMasternodeMetaInfo::ToJson() const 39 : { 40 3758 : int64_t now = GetTime<std::chrono::seconds>().count(); 41 : 42 3758 : UniValue ret(UniValue::VOBJ); 43 3758 : ret.pushKV("lastDSQ", m_last_dsq); 44 3758 : ret.pushKV("mixingTxCount", m_mixing_tx_count); 45 3758 : ret.pushKV("outboundAttemptCount", outboundAttemptCount); 46 3758 : ret.pushKV("lastOutboundAttempt", lastOutboundAttempt); 47 3758 : ret.pushKV("lastOutboundAttemptElapsed", now - lastOutboundAttempt); 48 3758 : ret.pushKV("lastOutboundSuccess", lastOutboundSuccess); 49 3758 : ret.pushKV("lastOutboundSuccessElapsed", now - lastOutboundSuccess); 50 3758 : ret.pushKV("is_platform_banned", m_platform_ban); 51 3758 : ret.pushKV("platform_ban_height_updated", m_platform_ban_updated); 52 : 53 3758 : return ret; 54 3758 : } 55 : 56 1480 : 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 1480 : const auto& pair = mapGovernanceObjectsVotedOn.emplace(nGovernanceObjectHash, 0); 61 1480 : pair.first->second++; 62 1480 : } 63 : 64 300 : void CMasternodeMetaInfo::RemoveGovernanceObject(const uint256& nGovernanceObjectHash) 65 : { 66 : // Whether or not the govobj hash exists in the map first is irrelevant. 67 300 : mapGovernanceObjectsVotedOn.erase(nGovernanceObjectHash); 68 300 : } 69 : 70 22676 : const CMasternodeMetaInfo& CMasternodeMetaMan::GetMetaInfoOrDefault(const uint256& protx_hash) const 71 : { 72 22676 : const auto it = metaInfos.find(protx_hash); 73 22676 : if (it == metaInfos.end()) return default_meta_info; 74 17407 : return it->second; 75 22676 : } 76 : 77 3758 : CMasternodeMetaInfo CMasternodeMetaMan::GetInfo(const uint256& proTxHash) const 78 : { 79 3758 : LOCK(cs); 80 3758 : return GetMetaInfoOrDefault(proTxHash); 81 3758 : } 82 : 83 7301 : CMasternodeMetaInfo& CMasternodeMetaMan::GetMetaInfo(const uint256& proTxHash) 84 : { 85 7301 : auto it = metaInfos.find(proTxHash); 86 7301 : if (it != metaInfos.end()) { 87 6010 : return it->second; 88 : } 89 1291 : it = metaInfos.emplace(proTxHash, CMasternodeMetaInfo{proTxHash}).first; 90 1291 : return it->second; 91 7301 : } 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 1480 : void CMasternodeMetaMan::AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash) 131 : { 132 1480 : LOCK(cs); 133 1480 : GetMetaInfo(proTxHash).AddGovernanceVote(nGovernanceObjectHash); 134 1480 : } 135 : 136 60 : void CMasternodeMetaMan::RemoveGovernanceObject(const uint256& nGovernanceObjectHash) 137 : { 138 60 : LOCK(cs); 139 360 : for (auto& [_, meta_info] : metaInfos) { 140 300 : meta_info.RemoveGovernanceObject(nGovernanceObjectHash); 141 : } 142 60 : } 143 : 144 99040 : std::vector<uint256> CMasternodeMetaMan::GetAndClearDirtyGovernanceObjectHashes() 145 : { 146 99040 : std::vector<uint256> vecTmp; 147 198080 : WITH_LOCK(cs, vecTmp.swap(vecDirtyGovernanceObjectHashes)); 148 99040 : return vecTmp; 149 99040 : } 150 : 151 3545 : void CMasternodeMetaMan::SetLastOutboundAttempt(const uint256& protx_hash, int64_t t) 152 : { 153 3545 : LOCK(cs); 154 3545 : GetMetaInfo(protx_hash).SetLastOutboundAttempt(t); 155 3545 : } 156 : 157 2266 : void CMasternodeMetaMan::SetLastOutboundSuccess(const uint256& protx_hash, int64_t t) 158 : { 159 2266 : LOCK(cs); 160 2266 : GetMetaInfo(protx_hash).SetLastOutboundSuccess(t); 161 2266 : } 162 : 163 14229 : int64_t CMasternodeMetaMan::GetLastOutboundAttempt(const uint256& protx_hash) const 164 : { 165 14229 : LOCK(cs); 166 14229 : return GetMetaInfoOrDefault(protx_hash).lastOutboundAttempt; 167 14229 : } 168 : 169 1266 : int64_t CMasternodeMetaMan::GetLastOutboundSuccess(const uint256& protx_hash) const 170 : { 171 1266 : LOCK(cs); 172 1266 : return GetMetaInfoOrDefault(protx_hash).lastOutboundSuccess; 173 1266 : } 174 : 175 2166 : bool CMasternodeMetaMan::OutboundFailedTooManyTimes(const uint256& protx_hash) const 176 : { 177 2166 : LOCK(cs); 178 2166 : return GetMetaInfoOrDefault(protx_hash).outboundAttemptCount > MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS; 179 2166 : } 180 : 181 1257 : bool CMasternodeMetaMan::IsPlatformBanned(const uint256& protx_hash) const 182 : { 183 1257 : LOCK(cs); 184 1257 : return GetMetaInfoOrDefault(protx_hash).m_platform_ban; 185 1257 : } 186 : 187 2163 : bool CMasternodeMetaMan::ResetPlatformBan(const uint256& protx_hash, int height) 188 : { 189 2163 : LOCK(cs); 190 : 191 2163 : auto it = metaInfos.find(protx_hash); 192 2163 : if (it == metaInfos.end()) return false; 193 : 194 51 : return it->second.SetPlatformBan(false, height); 195 2163 : } 196 : 197 10 : bool CMasternodeMetaMan::SetPlatformBan(const uint256& inv_hash, PlatformBanMessage&& ban_msg) 198 : { 199 10 : LOCK(cs); 200 : 201 10 : const uint256& protx_hash = ban_msg.m_protx_hash; 202 : 203 10 : bool ret = GetMetaInfo(protx_hash).SetPlatformBan(true, ban_msg.m_requested_height); 204 10 : if (ret) { 205 10 : m_seen_platform_bans.emplace(inv_hash, std::move(ban_msg)); 206 10 : } 207 10 : return ret; 208 10 : } 209 : 210 19 : bool CMasternodeMetaMan::AlreadyHavePlatformBan(const uint256& inv_hash) const 211 : { 212 19 : LOCK(cs); 213 19 : return m_seen_platform_bans.exists(inv_hash); 214 19 : } 215 : 216 10 : std::optional<PlatformBanMessage> CMasternodeMetaMan::GetPlatformBan(const uint256& inv_hash) const 217 : { 218 10 : LOCK(cs); 219 10 : PlatformBanMessage ret; 220 10 : if (!m_seen_platform_bans.get(inv_hash, ret)) { 221 0 : return std::nullopt; 222 : } 223 : 224 10 : return ret; 225 10 : } 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 7204 : std::string MasternodeMetaStore::ToString() const 261 : { 262 7204 : LOCK(cs); 263 7204 : return strprintf("Masternodes: meta infos object count: %d, nDsqCount: %d, used masternodes count: %d", 264 7204 : metaInfos.size(), nDsqCount, m_used_masternodes.size()); 265 7204 : } 266 : 267 10 : uint256 PlatformBanMessage::GetHash() const { return ::SerializeHash(*this); }