diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index c08a647cac7ee..23c1789d80ba9 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -116,17 +116,11 @@ MessageProcessingResult CCoinJoinClientQueueManager::ProcessMessage(NodeId from, LogPrint(BCLog::COINJOIN, "DSQUEUE -- CoinJoin queue is ready, masternode=%s, queue=%s\n", dmn->proTxHash.ToString(), dsq.ToString()); return ret; } else { - int64_t nLastDsq = m_mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = m_mn_metaman.GetDsqThreshold(dmn->proTxHash, tip_mn_list.GetValidMNsCount()); - LogPrint(BCLog::COINJOIN, "DSQUEUE -- nLastDsq: %d nDsqThreshold: %d nDsqCount: %d\n", nLastDsq, - nDsqThreshold, m_mn_metaman.GetDsqCount()); - // don't allow a few nodes to dominate the queuing process - if (nLastDsq != 0 && nDsqThreshold > m_mn_metaman.GetDsqCount()) { + if (m_mn_metaman.IsDsqOver(dmn->proTxHash, tip_mn_list.GetValidMNsCount())) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- Masternode %s is sending too many dsq messages\n", dmn->proTxHash.ToString()); return ret; } - m_mn_metaman.AllowMixing(dmn->proTxHash); LogPrint(BCLog::COINJOIN, "DSQUEUE -- new CoinJoin queue, masternode=%s, queue=%s\n", dmn->proTxHash.ToString(), dsq.ToString()); @@ -1169,13 +1163,10 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon continue; } - int64_t nLastDsq = m_mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = m_mn_metaman.GetDsqThreshold(dmn->proTxHash, nMnCount); - if (nLastDsq != 0 && nDsqThreshold > m_mn_metaman.GetDsqCount()) { + if (m_mn_metaman.IsDsqOver(dmn->proTxHash, nMnCount)) { WalletCJLogPrint(m_wallet, /* Continued */ - "CCoinJoinClientSession::StartNewQueue -- too early to mix with node," /* Continued */ - " masternode=%s, nLastDsq=%d, nDsqThreshold=%d, nDsqCount=%d\n", - dmn->proTxHash.ToString(), nLastDsq, nDsqThreshold, m_mn_metaman.GetDsqCount()); + "CCoinJoinClientSession::StartNewQueue -- too early to mix with node masternode=%s\n", + dmn->proTxHash.ToString()); nTries++; continue; } diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index 70c5b4772109f..87cb7a46b06ed 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -78,9 +78,7 @@ void CCoinJoinServer::ProcessDSACCEPT(CNode& peer, CDataStream& vRecv) } } - int64_t nLastDsq = m_mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = m_mn_metaman.GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); - if (nLastDsq != 0 && nDsqThreshold > m_mn_metaman.GetDsqCount()) { + if (m_mn_metaman.IsDsqOver(dmn->proTxHash, mnList.GetValidMNsCount())) { if (fLogIPs) { LogPrint(BCLog::COINJOIN, "DSACCEPT -- last dsq too recent, must wait: peer=%d, addr=%s\n", peer.GetId(), peer.addr.ToStringAddrPort()); @@ -173,11 +171,8 @@ MessageProcessingResult CCoinJoinServer::ProcessDSQUEUE(NodeId from, CDataStream } if (!dsq.fReady) { - int64_t nLastDsq = m_mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = m_mn_metaman.GetDsqThreshold(dmn->proTxHash, tip_mn_list.GetValidMNsCount()); - LogPrint(BCLog::COINJOIN, "DSQUEUE -- nLastDsq: %d nDsqThreshold: %d nDsqCount: %d\n", nLastDsq, nDsqThreshold, m_mn_metaman.GetDsqCount()); //don't allow a few nodes to dominate the queuing process - if (nLastDsq != 0 && nDsqThreshold > m_mn_metaman.GetDsqCount()) { + if (m_mn_metaman.IsDsqOver(dmn->proTxHash, tip_mn_list.GetValidMNsCount())) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- node sending too many dsq messages, masternode=%s\n", dmn->proTxHash.ToString()); return ret; } diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index edd30d189b285..54b531dfb10ad 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -668,8 +668,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null(tx); if (!opt_proTx) continue; // should not happen but does not matter - if (auto meta_info = m_mn_metaman.GetMetaInfo(opt_proTx->proTxHash, false); - !meta_info || !meta_info->SetPlatformBan(false, nHeight)) { + if (m_mn_metaman.ResetPlatformBan(opt_proTx->proTxHash, nHeight)) { LogPrint(BCLog::LLMQ, "%s -- MN %s is failed to Platform revived at height %d\n", __func__, opt_proTx->proTxHash.ToString(), nHeight); } diff --git a/src/evo/mnauth.cpp b/src/evo/mnauth.cpp index 376c96ecfaf40..4d813ebbbf00f 100644 --- a/src/evo/mnauth.cpp +++ b/src/evo/mnauth.cpp @@ -103,7 +103,7 @@ MessageProcessingResult CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_s } if (!peer.IsInboundConn()) { - mn_metaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetTime().count()); + mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash, GetTime().count()); if (peer.m_masternode_probe_connection) { LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n", mnauth.proRegTxHash.ToString(), peer.GetId()); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 8945ec62f7fae..359d1170c4bf9 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -148,14 +148,7 @@ bool CGovernanceObject::ProcessVote(CMasternodeMetaMan& mn_metaman, CGovernanceM return false; } - if (!mn_metaman.AddGovernanceVote(dmn->proTxHash, vote.GetParentHash())) { - std::string msg{strprintf("CGovernanceObject::%s -- Unable to add governance vote, MN outpoint = %s, " - "governance object hash = %s", - __func__, vote.GetMasternodeOutpoint().ToStringShort(), GetHash().ToString())}; - LogPrint(BCLog::GOBJECT, "%s\n", msg); - exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_PERMANENT_ERROR); - return false; - } + mn_metaman.AddGovernanceVote(dmn->proTxHash, vote.GetParentHash()); voteInstanceRef = vote_instance_t(vote.GetOutcome(), nVoteTimeUpdate, vote.GetTimestamp()); fileVotes.AddVote(vote); diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index 062040a819a50..f7c6c4491771d 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -492,12 +492,11 @@ void CDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const m->badConnection = true; logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second); } - const auto meta_info = m_mn_metaman.GetMetaInfo(m->dmn->proTxHash); - if (meta_info->OutboundFailedTooManyTimes()) { + if (m_mn_metaman.OutboundFailedTooManyTimes(m->dmn->proTxHash)) { m->badConnection = true; logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString()); } - if (meta_info->IsPlatformBanned()) { + if (m_mn_metaman.IsPlatformBanned(m->dmn->proTxHash)) { m->badConnection = true; logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString()); } diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 09d2d4fddc7b9..099f5f74c9b98 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -912,7 +912,7 @@ void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, CConnman if (dmn->proTxHash == myProTxHash) { continue; } - auto lastOutbound = mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundSuccess(); + auto lastOutbound = mn_metaman.GetLastOutboundSuccess(dmn->proTxHash); if (curTime - lastOutbound < 10 * 60) { // avoid re-probing nodes too often continue; diff --git a/src/masternode/meta.cpp b/src/masternode/meta.cpp index adb2aaa302593..cfb1bc54c446f 100644 --- a/src/masternode/meta.cpp +++ b/src/masternode/meta.cpp @@ -10,6 +10,13 @@ const std::string MasternodeMetaStore::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-4"; +static constexpr int MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS{5}; +static constexpr int MASTERNODE_MAX_MIXING_TXES{5}; + +namespace { +static const CMasternodeMetaInfo default_meta_info_meta_info{}; +} // anonymous namespace + CMasternodeMetaMan::CMasternodeMetaMan() : m_db{std::make_unique("mncache.dat", "magicMasternodeCache")} { @@ -30,29 +37,24 @@ CMasternodeMetaMan::~CMasternodeMetaMan() UniValue CMasternodeMetaInfo::ToJson() const { - UniValue ret(UniValue::VOBJ); - int64_t now = GetTime().count(); - ret.pushKV("lastDSQ", nLastDsq.load()); - ret.pushKV("mixingTxCount", nMixingTxCount.load()); - ret.pushKV("outboundAttemptCount", outboundAttemptCount.load()); - ret.pushKV("lastOutboundAttempt", lastOutboundAttempt.load()); - ret.pushKV("lastOutboundAttemptElapsed", now - lastOutboundAttempt.load()); - ret.pushKV("lastOutboundSuccess", lastOutboundSuccess.load()); - ret.pushKV("lastOutboundSuccessElapsed", now - lastOutboundSuccess.load()); - { - LOCK(cs); - ret.pushKV("is_platform_banned", m_platform_ban); - ret.pushKV("platform_ban_height_updated", m_platform_ban_updated); - } + UniValue ret(UniValue::VOBJ); + ret.pushKV("lastDSQ", m_last_dsq); + ret.pushKV("mixingTxCount", m_mixing_tx_count); + ret.pushKV("outboundAttemptCount", outboundAttemptCount); + ret.pushKV("lastOutboundAttempt", lastOutboundAttempt); + ret.pushKV("lastOutboundAttemptElapsed", now - lastOutboundAttempt); + ret.pushKV("lastOutboundSuccess", lastOutboundSuccess); + ret.pushKV("lastOutboundSuccessElapsed", now - lastOutboundSuccess); + ret.pushKV("is_platform_banned", m_platform_ban); + ret.pushKV("platform_ban_height_updated", m_platform_ban_updated); return ret; } void CMasternodeMetaInfo::AddGovernanceVote(const uint256& nGovernanceObjectHash) { - LOCK(cs); // Insert a zero value, or not. Then increment the value regardless. This // ensures the value is in the map. const auto& pair = mapGovernanceObjectsVotedOn.emplace(nGovernanceObjectHash, 0); @@ -61,65 +63,83 @@ void CMasternodeMetaInfo::AddGovernanceVote(const uint256& nGovernanceObjectHash void CMasternodeMetaInfo::RemoveGovernanceObject(const uint256& nGovernanceObjectHash) { - LOCK(cs); // Whether or not the govobj hash exists in the map first is irrelevant. mapGovernanceObjectsVotedOn.erase(nGovernanceObjectHash); } -CMasternodeMetaInfoPtr CMasternodeMetaMan::GetMetaInfo(const uint256& proTxHash, bool fCreate) +const CMasternodeMetaInfo& CMasternodeMetaMan::GetMetaInfoOrDefault(const uint256& protx_hash) const +{ + const auto it = metaInfos.find(protx_hash); + if (it == metaInfos.end()) return default_meta_info_meta_info; + return it->second; +} + +CMasternodeMetaInfo CMasternodeMetaMan::GetInfo(const uint256& proTxHash) const +{ + LOCK (cs); + return GetMetaInfoOrDefault(proTxHash); +} + +CMasternodeMetaInfo& CMasternodeMetaMan::GetMetaInfo(const uint256& proTxHash) { - LOCK(cs); auto it = metaInfos.find(proTxHash); if (it != metaInfos.end()) { return it->second; } - if (!fCreate) { - return nullptr; - } - it = metaInfos.emplace(proTxHash, std::make_shared(proTxHash)).first; + it = metaInfos.emplace(proTxHash, CMasternodeMetaInfo{proTxHash}).first; return it->second; } -// We keep track of dsq (mixing queues) count to avoid using same masternodes for mixing too often. -// This threshold is calculated as the last dsq count this specific masternode was used in a mixing -// session plus a margin of 20% of masternode count. In other words we expect at least 20% of unique -// masternodes before we ever see a masternode that we know already mixed someone's funds earlier. -int64_t CMasternodeMetaMan::GetDsqThreshold(const uint256& proTxHash, int nMnCount) +bool CMasternodeMetaMan::IsDsqOver(const uint256& protx_hash, int mn_count) const { - auto metaInfo = GetMetaInfo(proTxHash); - if (metaInfo == nullptr) { - // return a threshold which is slightly above nDsqCount i.e. a no-go - return nDsqCount + 1; + LOCK(cs); + auto it = metaInfos.find(protx_hash); + if (it != metaInfos.end()) { + LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString()); + return false; } - return metaInfo->GetLastDsq() + nMnCount / 5; + const auto& meta_info = it->second; + int64_t last_dsq = meta_info.m_last_dsq; + int64_t threshold = last_dsq + mn_count / 5; + + LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n", + protx_hash.ToString(), last_dsq, threshold, nDsqCount); + return last_dsq != 0 && threshold > nDsqCount; } void CMasternodeMetaMan::AllowMixing(const uint256& proTxHash) { - auto mm = GetMetaInfo(proTxHash); nDsqCount++; - mm->nLastDsq = nDsqCount.load(); - mm->nMixingTxCount = 0; + + LOCK(cs); + auto& mm = GetMetaInfo(proTxHash); + mm.m_last_dsq = nDsqCount.load(); + mm.m_mixing_tx_count = 0; } void CMasternodeMetaMan::DisallowMixing(const uint256& proTxHash) { - auto mm = GetMetaInfo(proTxHash); - mm->nMixingTxCount++; + LOCK(cs); + GetMetaInfo(proTxHash).m_mixing_tx_count++; } -bool CMasternodeMetaMan::AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash) +bool CMasternodeMetaMan::IsValidForMixingTxes(const uint256& protx_hash) const { - auto mm = GetMetaInfo(proTxHash); - mm->AddGovernanceVote(nGovernanceObjectHash); - return true; + LOCK(cs); + return GetMetaInfoOrDefault(protx_hash).m_mixing_tx_count <= MASTERNODE_MAX_MIXING_TXES; +} + +void CMasternodeMetaMan::AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash) +{ + LOCK(cs); + GetMetaInfo(proTxHash).AddGovernanceVote(nGovernanceObjectHash); } void CMasternodeMetaMan::RemoveGovernanceObject(const uint256& nGovernanceObjectHash) { LOCK(cs); - for(const auto& p : metaInfos) { - p.second->RemoveGovernanceObject(nGovernanceObjectHash); + for (auto& p : metaInfos) { + p.second.RemoveGovernanceObject(nGovernanceObjectHash); } } @@ -130,6 +150,65 @@ std::vector CMasternodeMetaMan::GetAndClearDirtyGovernanceObjectHashes( return vecTmp; } +void CMasternodeMetaMan::SetLastOutboundAttempt(const uint256& protx_hash, int64_t t) +{ + LOCK(cs); + GetMetaInfo(protx_hash).SetLastOutboundAttempt(t); +} + +void CMasternodeMetaMan::SetLastOutboundSuccess(const uint256& protx_hash, int64_t t) +{ + LOCK(cs); + GetMetaInfo(protx_hash).SetLastOutboundSuccess(t); +} + +int64_t CMasternodeMetaMan::GetLastOutboundAttempt(const uint256& protx_hash) const +{ + LOCK(cs); + return GetMetaInfoOrDefault(protx_hash).lastOutboundAttempt; +} + +int64_t CMasternodeMetaMan::GetLastOutboundSuccess(const uint256& protx_hash) const +{ + LOCK(cs); + return GetMetaInfoOrDefault(protx_hash).lastOutboundSuccess; +} + +bool CMasternodeMetaMan::OutboundFailedTooManyTimes(const uint256& protx_hash) const +{ + LOCK(cs); + return GetMetaInfoOrDefault(protx_hash).outboundAttemptCount > MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS; +} + +bool CMasternodeMetaMan::IsPlatformBanned(const uint256& protx_hash) const +{ + LOCK(cs); + return GetMetaInfoOrDefault(protx_hash).m_platform_ban; +} + +bool CMasternodeMetaMan::ResetPlatformBan(const uint256& protx_hash, int height) +{ + LOCK(cs); + + auto it = metaInfos.find(protx_hash); + if (it == metaInfos.end()) return false; + + return it->second.SetPlatformBan(false, height); +} + +bool CMasternodeMetaMan::SetPlatformBan(const uint256& inv_hash, PlatformBanMessage&& ban_msg) +{ + LOCK(cs); + + const uint256& protx_hash = ban_msg.m_protx_hash; + + bool ret = GetMetaInfo(protx_hash).SetPlatformBan(true, ban_msg.m_requested_height); + if (ret) { + m_seen_platform_bans.insert(inv_hash, std::move(ban_msg)); + } + return ret; +} + bool CMasternodeMetaMan::AlreadyHavePlatformBan(const uint256& inv_hash) const { LOCK(cs); @@ -147,12 +226,6 @@ std::optional CMasternodeMetaMan::GetPlatformBan(const uint2 return ret; } -void CMasternodeMetaMan::RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg) -{ - LOCK(cs); - m_seen_platform_bans.insert(inv_hash, std::move(msg)); -} - std::string MasternodeMetaStore::ToString() const { LOCK(cs); diff --git a/src/masternode/meta.h b/src/masternode/meta.h index 217b9d800287d..13cb0bddfded1 100644 --- a/src/masternode/meta.h +++ b/src/masternode/meta.h @@ -13,105 +13,64 @@ #include #include -#include #include -#include #include #include -class CConnman; class UniValue; template class CFlatDB; -static constexpr int MASTERNODE_MAX_MIXING_TXES{5}; -static constexpr int MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS{5}; - // Holds extra (non-deterministic) information about masternodes // This is mostly local information, e.g. about mixing and governance class CMasternodeMetaInfo { - friend class CMasternodeMetaMan; - -private: - mutable Mutex cs; - - uint256 proTxHash GUARDED_BY(cs); +public: + uint256 m_protx_hash; - //the dsq count from the last dsq broadcast of this node - std::atomic nLastDsq{0}; - std::atomic nMixingTxCount{0}; + //! the dsq count from the last dsq broadcast of this node + int64_t m_last_dsq{0}; + int m_mixing_tx_count{0}; // KEEP TRACK OF GOVERNANCE ITEMS EACH MASTERNODE HAS VOTE UPON FOR RECALCULATION - std::map mapGovernanceObjectsVotedOn GUARDED_BY(cs); + std::map mapGovernanceObjectsVotedOn; - std::atomic outboundAttemptCount{0}; - std::atomic lastOutboundAttempt{0}; - std::atomic lastOutboundSuccess{0}; + int outboundAttemptCount{0}; + int64_t lastOutboundAttempt{0}; + int64_t lastOutboundSuccess{0}; //! bool flag is node currently under platform ban by p2p message - bool m_platform_ban GUARDED_BY(cs){false}; + bool m_platform_ban{false}; //! height at which platform ban has been applied or removed - int m_platform_ban_updated GUARDED_BY(cs){0}; + int m_platform_ban_updated{0}; public: CMasternodeMetaInfo() = default; - explicit CMasternodeMetaInfo(const uint256& _proTxHash) : proTxHash(_proTxHash) {} - CMasternodeMetaInfo(const CMasternodeMetaInfo& ref) : - proTxHash(ref.proTxHash), - nLastDsq(ref.nLastDsq.load()), - nMixingTxCount(ref.nMixingTxCount.load()), - mapGovernanceObjectsVotedOn(ref.mapGovernanceObjectsVotedOn), - lastOutboundAttempt(ref.lastOutboundAttempt.load()), - lastOutboundSuccess(ref.lastOutboundSuccess.load()), - m_platform_ban(ref.m_platform_ban), - m_platform_ban_updated(ref.m_platform_ban_updated) - { - } - - template - void Serialize(Stream& s) const EXCLUSIVE_LOCKS_REQUIRED(!cs) + explicit CMasternodeMetaInfo(const uint256& protx_hash) : + m_protx_hash(protx_hash) { - LOCK(cs); - s << proTxHash << nLastDsq << nMixingTxCount << mapGovernanceObjectsVotedOn << outboundAttemptCount - << lastOutboundAttempt << lastOutboundSuccess << m_platform_ban << m_platform_ban_updated; } + CMasternodeMetaInfo(const CMasternodeMetaInfo& ref) = default; - template - void Unserialize(Stream& s) EXCLUSIVE_LOCKS_REQUIRED(!cs) + SERIALIZE_METHODS(CMasternodeMetaInfo, obj) { - LOCK(cs); - s >> proTxHash >> nLastDsq >> nMixingTxCount >> mapGovernanceObjectsVotedOn >> outboundAttemptCount >> - lastOutboundAttempt >> lastOutboundSuccess >> m_platform_ban >> m_platform_ban_updated; + READWRITE(obj.m_protx_hash, obj.m_last_dsq, obj.m_mixing_tx_count, obj.mapGovernanceObjectsVotedOn, + obj.outboundAttemptCount, obj.lastOutboundAttempt, obj.lastOutboundSuccess, obj.m_platform_ban, + obj.m_platform_ban_updated); } - UniValue ToJson() const EXCLUSIVE_LOCKS_REQUIRED(!cs); - -public: - const uint256 GetProTxHash() const EXCLUSIVE_LOCKS_REQUIRED(!cs) - { - LOCK(cs); - return proTxHash; - } - int64_t GetLastDsq() const { return nLastDsq; } - int GetMixingTxCount() const { return nMixingTxCount; } - - bool IsValidForMixingTxes() const { return GetMixingTxCount() <= MASTERNODE_MAX_MIXING_TXES; } + UniValue ToJson() const; // KEEP TRACK OF EACH GOVERNANCE ITEM IN CASE THIS NODE GOES OFFLINE, SO WE CAN RECALCULATE THEIR STATUS - void AddGovernanceVote(const uint256& nGovernanceObjectHash) EXCLUSIVE_LOCKS_REQUIRED(!cs); - - void RemoveGovernanceObject(const uint256& nGovernanceObjectHash) EXCLUSIVE_LOCKS_REQUIRED(!cs); + void AddGovernanceVote(const uint256& nGovernanceObjectHash); + void RemoveGovernanceObject(const uint256& nGovernanceObjectHash); - bool OutboundFailedTooManyTimes() const { return outboundAttemptCount > MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS; } void SetLastOutboundAttempt(int64_t t) { lastOutboundAttempt = t; ++outboundAttemptCount; } - int64_t GetLastOutboundAttempt() const { return lastOutboundAttempt; } void SetLastOutboundSuccess(int64_t t) { lastOutboundSuccess = t; outboundAttemptCount = 0; } - int64_t GetLastOutboundSuccess() const { return lastOutboundSuccess; } - bool SetPlatformBan(bool is_banned, int height) EXCLUSIVE_LOCKS_REQUIRED(!cs) + + bool SetPlatformBan(bool is_banned, int height) { - LOCK(cs); if (height < m_platform_ban_updated) { return false; } @@ -122,13 +81,7 @@ class CMasternodeMetaInfo m_platform_ban_updated = height; return true; } - bool IsPlatformBanned() const EXCLUSIVE_LOCKS_REQUIRED(!cs) - { - LOCK(cs); - return m_platform_ban; - } }; -using CMasternodeMetaInfoPtr = std::shared_ptr; class MasternodeMetaStore { @@ -136,7 +89,7 @@ class MasternodeMetaStore static const std::string SERIALIZATION_VERSION_STRING; mutable Mutex cs; - std::map metaInfos GUARDED_BY(cs); + std::map metaInfos GUARDED_BY(cs); // keep track of dsq count to prevent masternodes from gaming coinjoin queue std::atomic nDsqCount{0}; @@ -147,7 +100,7 @@ class MasternodeMetaStore LOCK(cs); std::vector tmpMetaInfo; for (const auto& p : metaInfos) { - tmpMetaInfo.emplace_back(*p.second); + tmpMetaInfo.emplace_back(p.second); } s << SERIALIZATION_VERSION_STRING << tmpMetaInfo << nDsqCount; } @@ -155,9 +108,9 @@ class MasternodeMetaStore template void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs) { - Clear(); - LOCK(cs); + + metaInfos.clear(); std::string strVersion; s >> strVersion; if (strVersion != SERIALIZATION_VERSION_STRING) { @@ -165,9 +118,8 @@ class MasternodeMetaStore } std::vector tmpMetaInfo; s >> tmpMetaInfo >> nDsqCount; - metaInfos.clear(); for (auto& mm : tmpMetaInfo) { - metaInfos.emplace(mm.GetProTxHash(), std::make_shared(std::move(mm))); + metaInfos.emplace(mm.m_protx_hash, CMasternodeMetaInfo{std::move(mm)}); } } @@ -233,6 +185,9 @@ class CMasternodeMetaMan : public MasternodeMetaStore mutable unordered_lru_cache m_seen_platform_bans GUARDED_BY(cs){ SeenBanInventorySize}; + CMasternodeMetaInfo& GetMetaInfo(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs); + const CMasternodeMetaInfo& GetMetaInfoOrDefault(const uint256& proTxHash) const EXCLUSIVE_LOCKS_REQUIRED(cs); + public: explicit CMasternodeMetaMan(); ~CMasternodeMetaMan(); @@ -241,22 +196,34 @@ class CMasternodeMetaMan : public MasternodeMetaStore bool IsValid() const { return is_valid; } - CMasternodeMetaInfoPtr GetMetaInfo(const uint256& proTxHash, bool fCreate = true) EXCLUSIVE_LOCKS_REQUIRED(!cs); + CMasternodeMetaInfo GetInfo(const uint256& proTxHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - int64_t GetDsqCount() const { return nDsqCount; } - int64_t GetDsqThreshold(const uint256& proTxHash, int nMnCount); + // We keep track of dsq (mixing queues) count to avoid using same masternodes for mixing too often. + // MN's threshold is calculated as the last dsq count this specific masternode was used in a mixing + // session plus a margin of 20% of masternode count. In other words we expect at least 20% of unique + // masternodes before we ever see a masternode that we know already mixed someone's funds earlier. + bool IsDsqOver(const uint256& protx_hash, int mn_count) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - void AllowMixing(const uint256& proTxHash); - void DisallowMixing(const uint256& proTxHash); + void AllowMixing(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(!cs); + void DisallowMixing(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool IsValidForMixingTxes(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash); + void AddGovernanceVote(const uint256& proTxHash, const uint256& nGovernanceObjectHash); void RemoveGovernanceObject(const uint256& nGovernanceObjectHash) EXCLUSIVE_LOCKS_REQUIRED(!cs); std::vector GetAndClearDirtyGovernanceObjectHashes() EXCLUSIVE_LOCKS_REQUIRED(!cs); + void SetLastOutboundAttempt(const uint256& protx_hash, int64_t t) EXCLUSIVE_LOCKS_REQUIRED(!cs); + void SetLastOutboundSuccess(const uint256& protx_hash, int64_t t) EXCLUSIVE_LOCKS_REQUIRED(!cs); + int64_t GetLastOutboundAttempt(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); + int64_t GetLastOutboundSuccess(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool OutboundFailedTooManyTimes(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); + + bool IsPlatformBanned(const uint256& protx_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool ResetPlatformBan(const uint256& protx_hash, int height) EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool SetPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg) EXCLUSIVE_LOCKS_REQUIRED(!cs); bool AlreadyHavePlatformBan(const uint256& inv_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); std::optional GetPlatformBan(const uint256& inv_hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - void RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg) EXCLUSIVE_LOCKS_REQUIRED(!cs); }; #endif // BITCOIN_MASTERNODE_META_H diff --git a/src/net.cpp b/src/net.cpp index ea96b1162a403..4f51ec2cada14 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3442,7 +3442,7 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman, continue; } if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) { - int64_t lastAttempt = mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); + int64_t lastAttempt = mn_metaman.GetLastOutboundAttempt(dmn->proTxHash); // back off trying connecting to an address if we already tried recently if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) { continue; @@ -3466,14 +3466,14 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman, bool connectedAndOutbound = connectedProRegTxHashes.count(dmn->proTxHash) && !connectedProRegTxHashes[dmn->proTxHash]; if (connectedAndOutbound) { // we already have an outbound connection to this MN so there is no theed to probe it again - mn_metaman.GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(nANow); + mn_metaman.SetLastOutboundSuccess(dmn->proTxHash, nANow); it = masternodePendingProbes.erase(it); continue; } ++it; - int64_t lastAttempt = mn_metaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); + int64_t lastAttempt = mn_metaman.GetLastOutboundAttempt(dmn->proTxHash); // back off trying connecting to an address if we already tried recently if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) { continue; @@ -3524,7 +3524,7 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman, didConnect = true; - mn_metaman.GetMetaInfo(connectToDmn->proTxHash)->SetLastOutboundAttempt(nANow); + mn_metaman.SetLastOutboundAttempt(connectToDmn->proTxHash, nANow); OpenMasternodeConnection(CAddress(connectToDmn->pdmnState->netInfo->GetPrimary(), NODE_NETWORK), /*use_v2transport=*/GetLocalServices() & NODE_P2P_V2, isProbe); // should be in the list now if connection was opened @@ -3537,7 +3537,7 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman, if (!connected) { LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- connection failed for masternode %s, service=%s\n", __func__, connectToDmn->proTxHash.ToString(), connectToDmn->pdmnState->netInfo->GetPrimary().ToStringAddrPort()); // Will take a few consequent failed attempts to PoSe-punish a MN. - if (mn_metaman.GetMetaInfo(connectToDmn->proTxHash)->OutboundFailedTooManyTimes()) { + if (mn_metaman.OutboundFailedTooManyTimes(connectToDmn->proTxHash)) { LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- failed to connect to masternode %s too many times\n", __func__, connectToDmn->proTxHash.ToString()); } } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index bee912118e31a..3d49a5c463a46 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3477,7 +3477,7 @@ std::pair static ValidateDSTX(CDeterministicMN return {false, true}; } - if (!mn_metaman.GetMetaInfo(dmn->proTxHash)->IsValidForMixingTxes()) { + if (!mn_metaman.IsValidForMixingTxes(dmn->proTxHash)) { LogPrint(BCLog::COINJOIN, "DSTX -- Masternode %s is sending too many transactions %s\n", dstx.masternodeOutpoint.ToStringShort(), hashTx.ToString()); return {true, true}; // TODO: Not an error? Could it be that someone is relaying old DSTXes @@ -3605,10 +3605,8 @@ MessageProcessingResult PeerManagerImpl::ProcessPlatformBanMessage(NodeId node, } // At this point, the outgoing message serialization version can't change. - const auto meta_info = m_mn_metaman.GetMetaInfo(ban_msg.m_protx_hash); - if (meta_info->SetPlatformBan(true, ban_msg.m_requested_height)) { - LogPrintf("PLATFORMBAN -- forward message to other nodes\n"); - m_mn_metaman.RememberPlatformBan(hash, std::move(ban_msg)); + if (m_mn_metaman.SetPlatformBan(hash, std::move(ban_msg))) { + LogPrintf("PLATFORMBAN -- hash: %s forward message to other nodes\n", hash.ToString()); ret.m_inventory.emplace_back(MSG_PLATFORM_BAN, hash); } return ret; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 3e0dcc6416368..e5e921d70c85c 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1379,8 +1379,7 @@ static UniValue BuildDMNListEntry(const CWallet* const pwallet, const CDetermini } #endif - const auto metaInfo = mn_metaman.GetMetaInfo(dmn.proTxHash); - o.pushKV("metaInfo", metaInfo->ToJson()); + o.pushKV("metaInfo", mn_metaman.GetInfo(dmn.proTxHash).ToJson()); return o; }