Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
521a8e1
test: sort p2p messages list in p2p.py
knst Mar 18, 2025
775114e
test: removed on_feefilter that has never been implemented for Dash
knst Mar 18, 2025
fbf89d6
feat!: add new protocol version PLATFORM_BAN_VERSION = 70236
knst Mar 17, 2025
2cd44f0
feat!: implementation of Platform PoSe Ban p2p message
knst Mar 11, 2025
361991f
test: add platforban message to test framework
knst Mar 17, 2025
2dd8026
test: functional test p2p_platform_ban.py
knst Mar 17, 2025
f316bf9
fix: add missing LOCK for m_platform_ban
knst May 6, 2025
9b6597e
fix: add list of valid mns for mine_quorum helper in p2p_platform_ban.py
knst May 7, 2025
00e047d
fix: bump version in masternode meta store
knst Aug 15, 2025
24797be
refactor: use std::move to make a copy of PlatformBan message
knst Aug 18, 2025
7d39124
refactor: use LRU cache instead map to limit size of seen platform ba…
knst Aug 18, 2025
c739f3a
refactor: fixes based on code review and better documenting of classes
knst Aug 18, 2025
e73e620
refactor: rename argument name from from to node
knst Aug 19, 2025
497a04c
refactor: rename argument name from from to node II
knst Aug 19, 2025
853e9c8
refactor: rename day_of_blocks to PLATFORM_BAN_WINDOW_BLOCKS
knst Aug 19, 2025
6e39350
fix: add missing call of EraseObjectRequest for platform ban p2p message
knst Aug 19, 2025
71825d1
refactor: let PostProcessMessage do the job
knst Aug 19, 2025
e0d6327
refactor: let PostProcessMessage do the job II
knst Aug 19, 2025
e8b01f4
fix: return ret instead {}, missing usage
knst Aug 19, 2025
4221644
fix: misusing m_inventory for ProcessMessageRet
knst Aug 19, 2025
c687022
fix: enforce quorum hash while validation platform ban p2p message
knst Aug 19, 2025
c191c53
fix: no flipping ban/unban if it's the same height for p2p message fo…
knst Aug 22, 2025
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
20 changes: 19 additions & 1 deletion src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
#include <evo/providertx.h>
#include <evo/simplifiedmns.h>
#include <evo/specialtx.h>
#include <index/txindex.h>

#include <chainparams.h>
#include <coins.h>
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <index/txindex.h>
#include <masternode/meta.h>
#include <messagesigner.h>
#include <script/standard.h>
#include <stats/client.h>
Expand Down Expand Up @@ -639,6 +640,23 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<co
oldList = GetListForBlockInternal(pindex->pprev);
diff = oldList.BuildDiff(newList);

// apply platform unban for platform revive too
for (int i = 1; i < (int)block.vtx.size(); i++) {
const CTransaction& tx = *block.vtx[i];
if (!tx.IsSpecialTxVersion() || tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) {
// only interested in revive transactions
continue;
}
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)) {
LogPrint(BCLog::LLMQ, "%s -- MN %s is failed to Platform revived at height %d\n", __func__,
opt_proTx->proTxHash.ToString(), nHeight);
}
}

m_evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff);
if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || pindex->pprev == m_initial_snapshot_index) {
m_evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList);
Expand Down
7 changes: 5 additions & 2 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class CCoinsViewCache;
class CEvoDB;
class CSimplifiedMNList;
class CSimplifiedMNListEntry;
class CMasternodeMetaMan;
class TxValidationState;

extern RecursiveMutex cs_main;
Expand Down Expand Up @@ -602,15 +603,17 @@ class CDeterministicMNManager
std::atomic<int> to_cleanup {0};

CEvoDB& m_evoDb;
CMasternodeMetaMan& m_mn_metaman;

std::unordered_map<uint256, CDeterministicMNList, StaticSaltedHasher> mnListsCache GUARDED_BY(cs);
std::unordered_map<uint256, CDeterministicMNListDiff, StaticSaltedHasher> mnListDiffsCache GUARDED_BY(cs);
const CBlockIndex* tipIndex GUARDED_BY(cs) {nullptr};
const CBlockIndex* m_initial_snapshot_index GUARDED_BY(cs) {nullptr};

public:
explicit CDeterministicMNManager(CEvoDB& evoDb) :
m_evoDb(evoDb)
explicit CDeterministicMNManager(CEvoDB& evoDb, CMasternodeMetaMan& mn_metaman) :
m_evoDb(evoDb),
m_mn_metaman(mn_metaman)
{
}
~CDeterministicMNManager() = default;
Expand Down
8 changes: 6 additions & 2 deletions src/llmq/dkgsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,15 @@ 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);
}

if (m_mn_metaman.GetMetaInfo(m->dmn->proTxHash)->OutboundFailedTooManyTimes()) {
const auto meta_info = m_mn_metaman.GetMetaInfo(m->dmn->proTxHash);
if (meta_info->OutboundFailedTooManyTimes()) {
m->badConnection = true;
logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString());
}
if (meta_info->IsPlatformBanned()) {
m->badConnection = true;
logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString());
}
}
}

Expand Down
32 changes: 31 additions & 1 deletion src/masternode/meta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <univalue.h>
#include <util/time.h>

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

CMasternodeMetaMan::CMasternodeMetaMan() :
m_db{std::make_unique<db_type>("mncache.dat", "magicMasternodeCache")}
Expand Down Expand Up @@ -41,6 +41,11 @@ UniValue CMasternodeMetaInfo::ToJson() const
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);
}

return ret;
}
Expand Down Expand Up @@ -125,8 +130,33 @@ std::vector<uint256> CMasternodeMetaMan::GetAndClearDirtyGovernanceObjectHashes(
return vecTmp;
}

bool CMasternodeMetaMan::AlreadyHavePlatformBan(const uint256& inv_hash) const
{
LOCK(cs);
return m_seen_platform_bans.exists(inv_hash);
}

std::optional<PlatformBanMessage> CMasternodeMetaMan::GetPlatformBan(const uint256& inv_hash) const
{
LOCK(cs);
PlatformBanMessage ret;
if (!m_seen_platform_bans.get(inv_hash, ret)) {
return std::nullopt;
}

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);
return strprintf("Masternodes: meta infos object count: %d, nDsqCount: %d", metaInfos.size(), nDsqCount);
}

uint256 PlatformBanMessage::GetHash() const { return ::SerializeHash(*this); }
88 changes: 78 additions & 10 deletions src/masternode/meta.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
#ifndef BITCOIN_MASTERNODE_META_H
#define BITCOIN_MASTERNODE_META_H

#include <bls/bls.h>
#include <saltedhasher.h>
#include <serialize.h>
#include <sync.h>
#include <threadsafety.h>
#include <uint256.h>
#include <unordered_lru_cache.h>

#include <atomic>
#include <map>
#include <memory>
#include <optional>
#include <vector>

class CConnman;
Expand Down Expand Up @@ -46,6 +50,11 @@ class CMasternodeMetaInfo
std::atomic<int64_t> lastOutboundAttempt{0};
std::atomic<int64_t> lastOutboundSuccess{0};

//! bool flag is node currently under platform ban by p2p message
bool m_platform_ban GUARDED_BY(cs){false};
//! height at which platform ban has been applied or removed
int m_platform_ban_updated GUARDED_BY(cs){0};

public:
CMasternodeMetaInfo() = default;
explicit CMasternodeMetaInfo(const uint256& _proTxHash) : proTxHash(_proTxHash) {}
Expand All @@ -55,22 +64,18 @@ class CMasternodeMetaInfo
nMixingTxCount(ref.nMixingTxCount.load()),
mapGovernanceObjectsVotedOn(ref.mapGovernanceObjectsVotedOn),
lastOutboundAttempt(ref.lastOutboundAttempt.load()),
lastOutboundSuccess(ref.lastOutboundSuccess.load())
lastOutboundSuccess(ref.lastOutboundSuccess.load()),
m_platform_ban(ref.m_platform_ban),
m_platform_ban_updated(ref.m_platform_ban_updated)
{
}

SERIALIZE_METHODS(CMasternodeMetaInfo, obj)
{
LOCK(obj.cs);
READWRITE(
obj.proTxHash,
obj.nLastDsq,
obj.nMixingTxCount,
obj.mapGovernanceObjectsVotedOn,
obj.outboundAttemptCount,
obj.lastOutboundAttempt,
obj.lastOutboundSuccess
);
READWRITE(obj.proTxHash, obj.nLastDsq, obj.nMixingTxCount, obj.mapGovernanceObjectsVotedOn,
obj.outboundAttemptCount, obj.lastOutboundAttempt, obj.lastOutboundSuccess, obj.m_platform_ban,
obj.m_platform_ban_updated);
}

UniValue ToJson() const;
Expand All @@ -96,6 +101,24 @@ class CMasternodeMetaInfo
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)
{
LOCK(cs);
if (height <= m_platform_ban_updated) {
return false;
}
if (height == m_platform_ban_updated && !is_banned) {
Copy link

Choose a reason for hiding this comment

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

This condition is unreachable now because we'll bail out in if (height <= m_platform_ban_updated) { above (it was changed from if (height < m_platform_ban_height) { in c739f3a#diff-4b932ee0759e8f52676ccdd2f94fb3a39ee0652695edfa3822ecb3dcb7ef9f30R107)

Copy link

@UdjinM6 UdjinM6 Aug 19, 2025

Choose a reason for hiding this comment

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

This is still confusing to me tbh... So this condition is basically "evonode CAN be banned at the height it was unbanned but it CAN NOT be unbanned at the height it was banned", right? We need a test for this edge case I guess.

EDIT:
In other words, if we process a block with protxupserv and then we receive platformban for the same height - we mark the node as bad. But if we receive platformban from the future (and mark the node as bad) first and we process a block with protxupserv for the same height later - we keep the node as bad.

We do need to update dip-0031 accordingly imo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am not sure why exactly I implemented this one like that, I had plan to update DIP-0031 accordingly implementation, but now I can't remember any more what is exactly not the same as DIP-0031 so far as half year passed since I implemented it.

I definitely want to prevent flipping ban/unban status. I added one more commit which change logic a bit. Could you have a look to it?

return false;
}
m_platform_ban = is_banned;
m_platform_ban_updated = height;
return true;
}
bool IsPlatformBanned() const
{
LOCK(cs);
return m_platform_ban;
}
};
using CMasternodeMetaInfoPtr = std::shared_ptr<CMasternodeMetaInfo>;

Expand Down Expand Up @@ -150,6 +173,41 @@ class MasternodeMetaStore
std::string ToString() const;
};

/**
* Platform PoSe Ban are result in the node voting against the targeted evonode in all future DKG sessions until that targeted
*evonode has been successfully banned. Platform will initiate this ban process by passing relevant information to Core using RPC. See DIP-0031
*
* We use 2 main classes to manage Platform PoSe Ban
*
* PlatformBanMessage
* CMasternodeMetaInfo - a higher-level construct which store extra (non-deterministic) information about masternodes including platform ban status
**/

/**
* PlatformBanMessage - low-level constructs which present p2p message PlatformBan containing the protx hash, requested
* height to ban, and BLS data: quorum hash and bls signature
*/
class PlatformBanMessage
{
public:
uint256 m_protx_hash;
int32_t m_requested_height{0};
uint256 m_quorum_hash;
CBLSSignature m_signature;

PlatformBanMessage() = default;

SERIALIZE_METHODS(PlatformBanMessage, obj)
{
READWRITE(obj.m_protx_hash, obj.m_requested_height, obj.m_quorum_hash);
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(CBLSSignatureVersionWrapper(const_cast<CBLSSignature&>(obj.m_signature), false));
}
}

uint256 GetHash() const;
};
Comment on lines +187 to +206
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify signature validation for platform ban messages.

The PlatformBanMessage class handles serialization correctly, but ensure that signature validation is implemented in the corresponding cpp file. The class would benefit from a method to verify the signature against the hashed message data.


🏁 Script executed:

#!/bin/bash
# Check if there's signature validation logic for PlatformBanMessages
echo "Searching for PlatformBanMessage signature validation..."
rg -A 10 "bool.*PlatformBanMessage.*[Vv]alidat" --type cpp

Length of output: 183


Action: Add Signature Verification Logic for PlatformBanMessage

It appears that the current implementation in src/masternode/meta.h only handles serialization and hash generation (via GetHash()), but no signature validation method is present in the corresponding source file. To ensure robust security for platform ban messages, please add a dedicated method (for example, bool ValidateSignature() const) in the corresponding CPP file (likely src/masternode/meta.cpp) that verifies the signature (m_signature) against the message hash.

  • File to update: src/masternode/meta.cpp (or the appropriate implementation file for PlatformBanMessage)
  • Action required: Implement signature validation using the hashed message data (obtained by GetHash()).
  • Recommendation: Confirm that the cryptographic routine used to validate CBLSSignature is correctly integrated.


class CMasternodeMetaMan : public MasternodeMetaStore
{
private:
Expand All @@ -161,6 +219,12 @@ class CMasternodeMetaMan : public MasternodeMetaStore

std::vector<uint256> vecDirtyGovernanceObjectHashes GUARDED_BY(cs);

// equal to double of expected amount of all evo nodes, see DIP-0028
// it consumes no more than 1Mb of RAM but will cover extreme cases
static constexpr size_t SeenBanInventorySize = 900;
mutable unordered_lru_cache<uint256, PlatformBanMessage, StaticSaltedHasher> m_seen_platform_bans GUARDED_BY(cs){
SeenBanInventorySize};

public:
explicit CMasternodeMetaMan();
~CMasternodeMetaMan();
Expand All @@ -181,6 +245,10 @@ class CMasternodeMetaMan : public MasternodeMetaStore
void RemoveGovernanceObject(const uint256& nGovernanceObjectHash);

std::vector<uint256> GetAndClearDirtyGovernanceObjectHashes();

bool AlreadyHavePlatformBan(const uint256& inv_hash) const;
std::optional<PlatformBanMessage> GetPlatformBan(const uint256& inv_hash) const;
void RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg);
};
Comment on lines +246 to 249
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Const-correctness and API tweak for RememberPlatformBan

Take the message by const reference (no need for rvalue) and document that it refreshes existing entries.

Apply:

-    void RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg);
+    void RememberPlatformBan(const uint256& inv_hash, const PlatformBanMessage& msg);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool AlreadyHavePlatformBan(const uint256& inv_hash) const;
std::optional<PlatformBanMessage> GetPlatformBan(const uint256& inv_hash) const;
void RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage&& msg);
};
bool AlreadyHavePlatformBan(const uint256& inv_hash) const;
std::optional<PlatformBanMessage> GetPlatformBan(const uint256& inv_hash) const;
void RememberPlatformBan(const uint256& inv_hash, const PlatformBanMessage& msg);
};
🤖 Prompt for AI Agents
In src/masternode/meta.h around lines 239 to 242, change RememberPlatformBan to
take the message by const reference rather than an rvalue (i.e., use const
PlatformBanMessage& msg) and add a brief comment on the declaration that the
call inserts or refreshes an existing entry (refreshes timestamp/metadata when
present). Update the function declaration to use the const ref signature and add
the single-line documentation above it; ensure the corresponding implementation
is updated to accept a const reference as well.


#endif // BITCOIN_MASTERNODE_META_H
Loading
Loading