Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
94b362c
test: removed on_feefilter that has never been implemented for Dash
knst Mar 18, 2025
2322929
feat!: add new protocol version PLATFORM_BAN_VERSION = 70236
knst Mar 17, 2025
0f80a02
feat!: implementation of Platform PoSe Ban p2p message
knst Mar 11, 2025
73e0b3f
test: add platforban message to test framework
knst Mar 17, 2025
fbd0cf9
test: functional test p2p_platform_ban.py
knst Mar 17, 2025
c0621a7
fix: add missing LOCK for m_platform_ban
knst May 6, 2025
0cd4e42
fix: add list of valid mns for mine_quorum helper in p2p_platform_ban.py
knst May 7, 2025
5affcc9
fix: bump version in masternode meta store
knst Aug 15, 2025
4dd0830
refactor: use std::move to make a copy of PlatformBan message
knst Aug 18, 2025
6cb94d2
refactor: use LRU cache instead map to limit size of seen platform ba…
knst Aug 18, 2025
6d31876
refactor: fixes based on code review and better documenting of classes
knst Aug 18, 2025
ff705fc
refactor: rename argument name from from to node
knst Aug 19, 2025
4b4bcfd
refactor: rename argument name from from to node II
knst Aug 19, 2025
33c4e36
refactor: rename day_of_blocks to PLATFORM_BAN_WINDOW_BLOCKS
knst Aug 19, 2025
0860a12
fix: add missing call of EraseObjectRequest for platform ban p2p message
knst Aug 19, 2025
d221a65
refactor: let PostProcessMessage do the job
knst Aug 19, 2025
42f9d7c
refactor: let PostProcessMessage do the job II
knst Aug 19, 2025
2af2844
fix: return ret instead {}, missing usage
knst Aug 19, 2025
8ec5e20
fix: misusing m_inventory for ProcessMessageRet
knst Aug 19, 2025
0e0e1ce
fix: enforce quorum hash while validation platform ban p2p message
knst Aug 19, 2025
efc3a3d
fix: no flipping ban/unban if it's the same height for p2p message fo…
knst Aug 22, 2025
af6f2cc
fixup: prevent flipping "is banned" for the equal height
knst Aug 28, 2025
64d2f67
fmt: removed dub newline in p2p_platform_ban.py
knst Sep 1, 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 <node/blockstorage.h>
#include <script/standard.h>
Expand Down Expand Up @@ -641,6 +642,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 @@ -626,15 +627,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) {
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 +190 to +209
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 +249 to 252
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