Skip to content

Commit dff8eaa

Browse files
committed
feat!: implementation of Platform PoSe Ban p2p message
1 parent 5160c3f commit dff8eaa

File tree

9 files changed

+201
-17
lines changed

9 files changed

+201
-17
lines changed

src/evo/deterministicmns.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <evo/specialtx.h>
1111
#include <llmq/commitment.h>
1212
#include <llmq/utils.h>
13+
#include <masternode/meta.h>
1314

1415
#include <base58.h>
1516
#include <chainparams.h>
@@ -851,6 +852,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
851852
newState->platformP2PPort = opt_proTx->platformP2PPort;
852853
newState->platformHTTPPort = opt_proTx->platformHTTPPort;
853854
}
855+
if (auto meta_info = m_mn_metaman.GetMetaInfo(opt_proTx->proTxHash, false);
856+
!meta_info || !meta_info->SetPlatformBan(false, nHeight)) {
857+
LogPrintf("CDeterministicMNManager::%s -- MN %s is not Platform revived at height %d\n", __func__,
858+
opt_proTx->proTxHash.ToString(), nHeight);
859+
}
860+
854861
if (newState->IsBanned()) {
855862
// only revive when all keys are set
856863
if (newState->pubKeyOperator != CBLSLazyPublicKey() && !newState->keyIDVoting.IsNull() &&

src/evo/deterministicmns.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class CBlockIndex;
3131
class CChainState;
3232
class CCoinsViewCache;
3333
class CEvoDB;
34+
class CMasternodeMetaMan;
3435
class TxValidationState;
3536

3637
extern RecursiveMutex cs_main;
@@ -565,16 +566,18 @@ class CDeterministicMNManager
565566

566567
CChainState& m_chainstate;
567568
CEvoDB& m_evoDb;
569+
CMasternodeMetaMan& m_mn_metaman;
568570

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

574576
public:
575-
explicit CDeterministicMNManager(CChainState& chainstate, CEvoDB& evoDb) :
577+
explicit CDeterministicMNManager(CChainState& chainstate, CEvoDB& evoDb, CMasternodeMetaMan& mn_metaman) :
576578
m_chainstate(chainstate),
577-
m_evoDb(evoDb)
579+
m_evoDb(evoDb),
580+
m_mn_metaman(mn_metaman)
578581
{
579582
}
580583
~CDeterministicMNManager() = default;

src/llmq/dkgsession.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,15 @@ void CDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const
493493
m->badConnection = true;
494494
logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second);
495495
}
496-
497-
if (m_mn_metaman.GetMetaInfo(m->dmn->proTxHash)->OutboundFailedTooManyTimes()) {
496+
const auto meta_info = m_mn_metaman.GetMetaInfo(m->dmn->proTxHash);
497+
if (meta_info->OutboundFailedTooManyTimes()) {
498498
m->badConnection = true;
499499
logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString());
500500
}
501+
if (meta_info->IsPlatformBanned()) {
502+
m->badConnection = true;
503+
logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString());
504+
}
501505
}
502506
}
503507

src/masternode/meta.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ UniValue CMasternodeMetaInfo::ToJson() const
4343
ret.pushKV("lastOutboundAttemptElapsed", now - lastOutboundAttempt);
4444
ret.pushKV("lastOutboundSuccess", lastOutboundSuccess);
4545
ret.pushKV("lastOutboundSuccessElapsed", now - lastOutboundSuccess);
46+
ret.pushKV("platform_ban", m_platform_ban);
47+
ret.pushKV("platform_ban_updated", m_platform_ban_height);
4648

4749
return ret;
4850
}
@@ -127,6 +129,27 @@ std::vector<uint256> CMasternodeMetaMan::GetAndClearDirtyGovernanceObjectHashes(
127129
return vecTmp;
128130
}
129131

132+
bool CMasternodeMetaMan::AlreadyHavePlatformBan(const uint256& inv_hash) const
133+
{
134+
LOCK(cs);
135+
return m_seen_platform_bans.contains(inv_hash);
136+
}
137+
138+
std::optional<PlatformBanMessage> CMasternodeMetaMan::GetPlatformBan(const uint256& inv_hash) const
139+
{
140+
LOCK(cs);
141+
auto it = m_seen_platform_bans.find(inv_hash);
142+
if (it == m_seen_platform_bans.end()) return std::nullopt;
143+
144+
return it->second;
145+
}
146+
147+
void CMasternodeMetaMan::RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage& msg)
148+
{
149+
LOCK(cs);
150+
m_seen_platform_bans.insert({inv_hash, msg});
151+
}
152+
130153
std::string MasternodeMetaStore::ToString() const
131154
{
132155
std::ostringstream info;
@@ -135,3 +158,5 @@ std::string MasternodeMetaStore::ToString() const
135158
", nDsqCount: " << (int)nDsqCount;
136159
return info.str();
137160
}
161+
162+
uint256 PlatformBanMessage::GetHash() const { return ::SerializeHash(*this); }

src/masternode/meta.h

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_MASTERNODE_META_H
66
#define BITCOIN_MASTERNODE_META_H
77

8+
#include <bls/bls.h>
89
#include <serialize.h>
910
#include <sync.h>
1011
#include <threadsafety.h>
@@ -13,6 +14,7 @@
1314
#include <atomic>
1415
#include <map>
1516
#include <memory>
17+
#include <optional>
1618
#include <vector>
1719

1820
class CConnman;
@@ -46,6 +48,9 @@ class CMasternodeMetaInfo
4648
std::atomic<int64_t> lastOutboundAttempt{0};
4749
std::atomic<int64_t> lastOutboundSuccess{0};
4850

51+
bool m_platform_ban GUARDED_BY(cs){false};
52+
int m_platform_ban_height GUARDED_BY(cs){0};
53+
4954
public:
5055
CMasternodeMetaInfo() = default;
5156
explicit CMasternodeMetaInfo(const uint256& _proTxHash) : proTxHash(_proTxHash) {}
@@ -55,22 +60,18 @@ class CMasternodeMetaInfo
5560
nMixingTxCount(ref.nMixingTxCount.load()),
5661
mapGovernanceObjectsVotedOn(ref.mapGovernanceObjectsVotedOn),
5762
lastOutboundAttempt(ref.lastOutboundAttempt.load()),
58-
lastOutboundSuccess(ref.lastOutboundSuccess.load())
63+
lastOutboundSuccess(ref.lastOutboundSuccess.load()),
64+
m_platform_ban(ref.m_platform_ban),
65+
m_platform_ban_height(ref.m_platform_ban_height)
5966
{
6067
}
6168

6269
SERIALIZE_METHODS(CMasternodeMetaInfo, obj)
6370
{
6471
LOCK(obj.cs);
65-
READWRITE(
66-
obj.proTxHash,
67-
obj.nLastDsq,
68-
obj.nMixingTxCount,
69-
obj.mapGovernanceObjectsVotedOn,
70-
obj.outboundAttemptCount,
71-
obj.lastOutboundAttempt,
72-
obj.lastOutboundSuccess
73-
);
72+
READWRITE(obj.proTxHash, obj.nLastDsq, obj.nMixingTxCount, obj.mapGovernanceObjectsVotedOn,
73+
obj.outboundAttemptCount, obj.lastOutboundAttempt, obj.lastOutboundSuccess, obj.m_platform_ban,
74+
obj.m_platform_ban_height);
7475
}
7576

7677
UniValue ToJson() const;
@@ -96,6 +97,24 @@ class CMasternodeMetaInfo
9697
int64_t GetLastOutboundAttempt() const { return lastOutboundAttempt; }
9798
void SetLastOutboundSuccess(int64_t t) { lastOutboundSuccess = t; outboundAttemptCount = 0; }
9899
int64_t GetLastOutboundSuccess() const { return lastOutboundSuccess; }
100+
bool SetPlatformBan(bool is_banned, int height)
101+
{
102+
LOCK(cs);
103+
if (height < m_platform_ban_height) {
104+
return false;
105+
}
106+
if (height == m_platform_ban_height && !is_banned) {
107+
return false;
108+
}
109+
m_platform_ban = is_banned;
110+
m_platform_ban_height = height;
111+
return true;
112+
}
113+
bool IsPlatformBanned() const
114+
{
115+
LOCK(cs);
116+
return m_platform_ban;
117+
}
99118
};
100119
using CMasternodeMetaInfoPtr = std::shared_ptr<CMasternodeMetaInfo>;
101120

@@ -150,6 +169,40 @@ class MasternodeMetaStore
150169
std::string ToString() const;
151170
};
152171

172+
/**
173+
* Platform PoSe Ban are result in the node voting against the targeted evonode in all future DKG sessions until that targeted
174+
*evonode has been successfully banned. Platform will initiate this ban process by passing relevant information to Core using RPC. See DIP-0031
175+
*
176+
* We use 2 main classes to manage Platform PoSe Ban
177+
*
178+
* PlatformBanMessage
179+
* PlatformBanManager - a higher-level construct which store information about ban status
180+
**/
181+
182+
/**
183+
* PlatformBanMessage - low-level constructs which contain the m_protx_hash, m_requested_height, m_quorum_hash and m_signature
184+
*/
185+
class PlatformBanMessage
186+
{
187+
public:
188+
uint256 m_protx_hash;
189+
int32_t m_requested_height{0};
190+
uint256 m_quorum_hash;
191+
CBLSSignature m_signature;
192+
193+
PlatformBanMessage() = default;
194+
195+
SERIALIZE_METHODS(PlatformBanMessage, obj)
196+
{
197+
READWRITE(obj.m_protx_hash, obj.m_requested_height, obj.m_quorum_hash);
198+
if (!(s.GetType() & SER_GETHASH)) {
199+
READWRITE(CBLSSignatureVersionWrapper(const_cast<CBLSSignature&>(obj.m_signature), false));
200+
}
201+
}
202+
203+
uint256 GetHash() const;
204+
};
205+
153206
class CMasternodeMetaMan : public MasternodeMetaStore
154207
{
155208
private:
@@ -160,6 +213,7 @@ class CMasternodeMetaMan : public MasternodeMetaStore
160213
bool is_valid{false};
161214

162215
std::vector<uint256> vecDirtyGovernanceObjectHashes GUARDED_BY(cs);
216+
std::map<uint256, PlatformBanMessage> m_seen_platform_bans GUARDED_BY(cs);
163217

164218
public:
165219
explicit CMasternodeMetaMan();
@@ -181,6 +235,10 @@ class CMasternodeMetaMan : public MasternodeMetaStore
181235
void RemoveGovernanceObject(const uint256& nGovernanceObjectHash);
182236

183237
std::vector<uint256> GetAndClearDirtyGovernanceObjectHashes();
238+
239+
bool AlreadyHavePlatformBan(const uint256& inv_hash) const;
240+
std::optional<PlatformBanMessage> GetPlatformBan(const uint256& inv_hash) const;
241+
void RememberPlatformBan(const uint256& inv_hash, PlatformBanMessage& msg);
184242
};
185243

186244
#endif // BITCOIN_MASTERNODE_META_H

src/net_processing.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <util/check.h>
3939
#include <util/system.h>
4040
#include <util/strencodings.h>
41+
#include <util/underlying.h>
4142
#include <util/trace.h>
4243

4344
#include <algorithm>
@@ -712,6 +713,9 @@ class PeerManagerImpl final : public PeerManager
712713
const std::vector<CBlockHeader>& headers,
713714
bool via_compact_block)
714715
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
716+
PeerMsgRet ProcessPlatformBanMessage(CNode& peer, std::string_view msg_type, CDataStream& vRecv)
717+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
718+
715719
/** Various helpers for headers processing, invoked by ProcessHeadersMessage() */
716720
/** Deal with state tracking and headers sync for peers that send the
717721
* occasional non-connecting header (this can happen due to BIP 130 headers
@@ -2256,6 +2260,8 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv)
22562260
#else
22572261
return m_cj_ctx->server->HasQueue(inv.hash);
22582262
#endif
2263+
case MSG_PLATFORM_BAN:
2264+
return m_mn_metaman.AlreadyHavePlatformBan(inv.hash);
22592265
}
22602266

22612267

@@ -2878,6 +2884,13 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
28782884
push = true;
28792885
}
28802886
}
2887+
if (!push && inv.type == MSG_PLATFORM_BAN) {
2888+
auto opt_platform_ban = m_mn_metaman.GetPlatformBan(inv.hash);
2889+
if (opt_platform_ban.has_value()) {
2890+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PLATFORMBAN, *opt_platform_ban));
2891+
push = true;
2892+
}
2893+
}
28812894

28822895
if (!push) {
28832896
vNotFound.push_back(inv);
@@ -3506,6 +3519,74 @@ void PeerManagerImpl::PostProcessMessage(MessageProcessingResult&& result, NodeI
35063519
}
35073520
}
35083521

3522+
PeerMsgRet PeerManagerImpl::ProcessPlatformBanMessage(CNode& pfrom, std::string_view msg_type, CDataStream& vRecv)
3523+
{
3524+
if (msg_type != NetMsgType::PLATFORMBAN) return {};
3525+
3526+
// Do nothing if node is out of sync
3527+
if (!m_mn_sync.IsBlockchainSynced()) {
3528+
return {};
3529+
}
3530+
3531+
PlatformBanMessage ban_msg;
3532+
vRecv >> ban_msg;
3533+
3534+
const uint256 hash = ban_msg.GetHash();
3535+
3536+
LogPrintf("PLATFORMBAN -- hash: %s protx_hash: %s height: %d peer=%d\n", hash.ToString(), ban_msg.m_protx_hash.ToString(), ban_msg.m_requested_height, pfrom.GetId());
3537+
3538+
const auto list = Assert(m_dmnman)->GetListAtChainTip();
3539+
auto dmn = list.GetMN(ban_msg.m_protx_hash);
3540+
if (!dmn) {
3541+
// small P2P penalty (1), as the evonode may have very recently been removed
3542+
return tl::unexpected{1};
3543+
}
3544+
if (dmn->nType != MnType::Evo) {
3545+
// Ban node, P2P penalty (100) if protx_hash is associated with a regular node not an evonode
3546+
LogPrintf("PLATFORMBAN -- hash: %s protx_hash: %s unexpected type of node\n", hash.ToString(), ban_msg.m_protx_hash.ToString());
3547+
return tl::unexpected{100};
3548+
}
3549+
const int day_of_blocks = 576;
3550+
int tipHeight = WITH_LOCK(cs_main, return m_chainman.ActiveChainstate().m_chain.Height());
3551+
if (tipHeight < ban_msg.m_requested_height || tipHeight - day_of_blocks > ban_msg.m_requested_height) {
3552+
// m_requested_height is inside the range [TipHeight - 576 - 5, TipHeight + 5]
3553+
LogPrintf("PLATFORMBAN -- hash: %s protx_hash: %s unexpected height: %d tip: %d\n", hash.ToString(), ban_msg.m_protx_hash.ToString(), ban_msg.m_requested_height, tipHeight);
3554+
if (tipHeight + 5 < ban_msg.m_requested_height || tipHeight - day_of_blocks - 5 > ban_msg.m_requested_height) {
3555+
// m_requested_height is outside the range [TipHeight - 576 - 5, TipHeight + 5]
3556+
return tl::unexpected{10};
3557+
}
3558+
return tl::unexpected{1};
3559+
}
3560+
3561+
Consensus::LLMQType llmq_type = Params().GetConsensus().llmqTypePlatform;
3562+
auto quorum = m_llmq_ctx->qman->GetQuorum(llmq_type, ban_msg.m_quorum_hash);
3563+
if (!quorum) {
3564+
LogPrintf("PLATFORMBAN -- hash: %s protx_hash: %s missing quorum_hash: %s llmq_type: %d\n", hash.ToString(), ban_msg.m_protx_hash.ToString(), ban_msg.m_quorum_hash.ToString(), ToUnderlying(llmq_type));
3565+
return tl::unexpected{100};
3566+
}
3567+
3568+
const std::string PLATFORM_BAN_REQUESTID_PREFIX = "PlatformPoSeBan";
3569+
const auto data = std::make_pair(ban_msg.m_protx_hash, ban_msg.m_requested_height);
3570+
const uint256 request_id = ::SerializeHash(std::make_pair(PLATFORM_BAN_REQUESTID_PREFIX, data));
3571+
const uint256 msg_hash = ::SerializeHash(data);
3572+
3573+
auto ret = llmq::VerifyRecoveredSig(llmq_type, m_chainman.ActiveChainstate().m_chain, *m_llmq_ctx->qman, ban_msg.m_requested_height, request_id, msg_hash, ban_msg.m_signature);
3574+
if (ret != llmq::VerifyRecSigStatus::Valid) {
3575+
LogPrintf("PLATFORMBAN -- hash: %s protx_hash: %s request_id: %s msg_hash: %s sig validation failed: %d\n", hash.ToString(), ban_msg.m_protx_hash.ToString(), request_id.ToString(), msg_hash.ToString(), ToUnderlying(ret));
3576+
return tl::unexpected{100};
3577+
}
3578+
3579+
// At this point, the outgoing message serialization version can't change.
3580+
const auto meta_info = m_mn_metaman.GetMetaInfo(ban_msg.m_protx_hash);
3581+
if (meta_info->SetPlatformBan(true, ban_msg.m_requested_height)) {
3582+
LogPrintf("PLATFORMBAN -- forward message to other nodes\n");
3583+
m_mn_metaman.RememberPlatformBan(hash, ban_msg);
3584+
CInv platform_ban_inv{MSG_PLATFORM_BAN, hash};
3585+
RelayInv(platform_ban_inv);
3586+
}
3587+
return {};
3588+
}
3589+
35093590
void PeerManagerImpl::ProcessMessage(
35103591
CNode& pfrom,
35113592
const std::string& msg_type,
@@ -5218,7 +5299,6 @@ void PeerManagerImpl::ProcessMessage(
52185299
Misbehaving(pfrom.GetId(), 100, strprintf("received not-requested quorumrotationinfo. peer=%d", pfrom.GetId()));
52195300
return;
52205301
}
5221-
52225302
if (msg_type == NetMsgType::NOTFOUND) {
52235303
// Remove the NOTFOUND transactions from the peer
52245304
LOCK(cs_main);
@@ -5272,6 +5352,7 @@ void PeerManagerImpl::ProcessMessage(
52725352
ProcessPeerMsgRet(m_llmq_ctx->qman->ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom);
52735353
m_llmq_ctx->shareman->ProcessMessage(pfrom, *this, m_sporkman, msg_type, vRecv);
52745354
ProcessPeerMsgRet(m_llmq_ctx->sigman->ProcessMessage(pfrom, *this, msg_type, vRecv), pfrom);
5355+
ProcessPeerMsgRet(ProcessPlatformBanMessage(pfrom, msg_type, vRecv), pfrom);
52755356

52765357
if (msg_type == NetMsgType::CLSIG) {
52775358
if (llmq::AreChainLocksEnabled(m_sporkman)) {

src/node/chainstate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ void DashChainstateSetup(ChainstateManager& chainman,
213213
{
214214
// Same logic as pblocktree
215215
dmnman.reset();
216-
dmnman = std::make_unique<CDeterministicMNManager>(chainman.ActiveChainstate(), *evodb);
216+
dmnman = std::make_unique<CDeterministicMNManager>(chainman.ActiveChainstate(), *evodb, mn_metaman);
217217

218218
cpoolman.reset();
219219
cpoolman = std::make_unique<CCreditPoolManager>(*evodb);

0 commit comments

Comments
 (0)