Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions src/coinjoin/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,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());
Expand Down Expand Up @@ -1180,13 +1174,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;
}
Expand Down
9 changes: 2 additions & 7 deletions src/coinjoin/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,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());
Expand Down Expand Up @@ -194,11 +192,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;
}
Expand Down
3 changes: 1 addition & 2 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<co
const auto opt_proTx = GetTxPayload<CProUpServTx>(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);
}
Comment on lines +679 to 682
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the log message for platform ban revival.

The log message "is failed to Platform revived" is grammatically incorrect and confusing. Since this block now executes when the revival succeeds (not when it fails, as in the previous implementation), consider revising the message to something clearer like:

LogPrint(BCLog::LLMQ, "%s -- MN %s platform ban revived at height %d\n", __func__,
         opt_proTx->proTxHash.ToString(), nHeight);
🤖 Prompt for AI Agents
In src/evo/deterministicmns.cpp around lines 671-674, the LogPrint message is
grammatically incorrect and misleading ("is failed to Platform revived") and
should reflect that ResetPlatformBan succeeded; update the log string to a
clear, concise message such as "MN %s platform ban revived at height %d"
(preserve __func__ and the proTxHash.ToString() and nHeight parameters) so the
log accurately represents a successful revival.

Expand Down
2 changes: 1 addition & 1 deletion src/evo/mnauth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ MessageProcessingResult CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_s
}

if (!peer.IsInboundConn()) {
mn_metaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetTime<std::chrono::seconds>().count());
mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash, GetTime<std::chrono::seconds>().count());
if (peer.m_masternode_probe_connection) {
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n",
Comment on lines 105 to 108
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard SetLastOutboundSuccess with mn meta lock

SetLastOutboundSuccess requires the manager mutex to be held (the old code locked via GetMetaInfo). We’re now calling it bare, which violates the contract and will assert. Wrap the call in the manager’s lock (e.g. WITH_LOCK(mn_metaman.GetCs(), mn_metaman.SetLastOutboundSuccess(...));).

🤖 Prompt for AI Agents
In src/evo/mnauth.cpp around lines 105 to 108, SetLastOutboundSuccess is called
without holding mn_metaman's mutex which violates the manager's locking contract
and will assert; wrap the call with the manager lock, e.g. use
WITH_LOCK(mn_metaman.GetCs(),
mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash,
GetTime<std::chrono::seconds>().count())); and keep the rest of the logic
unchanged so the call is protected by the manager's mutex.

mnauth.proRegTxHash.ToString(), peer.GetId());
Expand Down
9 changes: 1 addition & 8 deletions src/governance/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,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);
Expand Down
5 changes: 2 additions & 3 deletions src/llmq/dkgsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
2 changes: 1 addition & 1 deletion src/llmq/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
169 changes: 121 additions & 48 deletions src/masternode/meta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@

const std::string MasternodeMetaStore::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-5";

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<db_type>("mncache.dat", "magicMasternodeCache")}
{
Expand All @@ -30,29 +37,24 @@ bool CMasternodeMetaMan::LoadCache(bool load_cache)

UniValue CMasternodeMetaInfo::ToJson() const
{
UniValue ret(UniValue::VOBJ);

int64_t now = GetTime<std::chrono::seconds>().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);
Expand All @@ -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)
{
auto it = metaInfos.find(proTxHash);
if (it != metaInfos.end()) {
return it->second;
}
if (!fCreate) {
return nullptr;
}
it = metaInfos.emplace(proTxHash, std::make_shared<CMasternodeMetaInfo>(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);
}
}

Expand All @@ -130,6 +150,65 @@ std::vector<uint256> 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);
Expand All @@ -147,12 +226,6 @@ std::optional<PlatformBanMessage> 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));
}

void CMasternodeMetaMan::AddUsedMasternode(const uint256& proTxHash)
{
LOCK(cs);
Expand Down
Loading
Loading