Various fixes for mixing queues (#3138)

* Always check for expired queues on masternodes

* Check if a queue is too old or too far into the future

Instead of only checking that it's to old

* Check that no masternode can spam us with dsqs regardless of dsq readiness
This commit is contained in:
UdjinM6 2019-10-09 19:48:32 +03:00 committed by GitHub
parent e0c56246f5
commit 152c10bc4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 36 deletions

View File

@ -54,12 +54,17 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string&
if (q == dsq) {
return;
}
if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way the same mn can send another dsq with the same readiness this soon
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- Peer %s is sending WAY too many dsq messages for a masternode with collateral %s\n", pfrom->GetLogString(), dsq.masternodeOutpoint.ToStringShort());
return;
}
}
} // cs_vecqueue
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- %s new\n", dsq.ToString());
if (dsq.IsExpired()) return;
if (dsq.IsTimeOutOfBounds()) return;
auto mnList = deterministicMNManager->GetListAtChainTip();
auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint);
@ -83,18 +88,6 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string&
}
}
} else {
LOCK(cs_deqsessions); // have to lock this first to avoid deadlocks with cs_vecqueue
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
for (const auto& q : vecPrivateSendQueue) {
if (q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way same mn can send another "not yet ready" dsq this soon
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", dmn->pdmnState->ToString());
return;
}
}
int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq();
int nThreshold = nLastDsq + mnList.GetValidMNsCount() / 5;
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", nLastDsq, nThreshold, mmetaman.GetDsqCount());
@ -107,12 +100,17 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string&
mmetaman.AllowMixing(dmn->proTxHash);
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString());
LOCK(cs_deqsessions);
for (auto& session : deqSessions) {
CDeterministicMNCPtr mnMixing;
if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing->collateralOutpoint == dsq.masternodeOutpoint) {
dsq.fTried = true;
}
}
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
vecPrivateSendQueue.push_back(dsq);
dsq.Relay(connman);
}

View File

@ -97,9 +97,6 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
}
} else if (strCommand == NetMsgType::DSQUEUE) {
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) {
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- peer=%d using obsolete version %i\n", pfrom->GetId(), pfrom->nVersion);
connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION)));
@ -109,16 +106,26 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
CPrivateSendQueue dsq;
vRecv >> dsq;
// process every dsq only once
for (const auto& q : vecPrivateSendQueue) {
if (q == dsq) {
return;
{
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
// process every dsq only once
for (const auto& q : vecPrivateSendQueue) {
if (q == dsq) {
return;
}
if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way the same mn can send another dsq with the same readiness this soon
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- Peer %s is sending WAY too many dsq messages for a masternode with collateral %s\n", pfrom->GetLogString(), dsq.masternodeOutpoint.ToStringShort());
return;
}
}
}
} // cs_vecqueue
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- %s new\n", dsq.ToString());
if (dsq.IsExpired()) return;
if (dsq.IsTimeOutOfBounds()) return;
auto mnList = deterministicMNManager->GetListAtChainTip();
auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint);
@ -131,14 +138,6 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
}
if (!dsq.fReady) {
for (const auto& q : vecPrivateSendQueue) {
if (q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way same mn can send another "not yet ready" dsq this soon
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", dmn->pdmnState->addr.ToString());
return;
}
}
int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq();
int nThreshold = nLastDsq + mnList.GetValidMNsCount() / 5;
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", nLastDsq, nThreshold, mmetaman.GetDsqCount());
@ -150,6 +149,9 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
mmetaman.AllowMixing(dmn->proTxHash);
LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString());
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
vecPrivateSendQueue.push_back(dsq);
dsq.Relay(connman);
}
@ -524,10 +526,11 @@ void CPrivateSendServer::ConsumeCollateral(CConnman& connman, const CTransaction
void CPrivateSendServer::CheckTimeout(CConnman& connman)
{
if (!fMasternodeMode) return;
if (nState == POOL_STATE_IDLE) return;
CheckQueue();
if (nState == POOL_STATE_IDLE) return;
int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT;
bool fTimeout = GetTime() - nTimeLastSuccessfulStep >= nTimeout;

View File

@ -82,6 +82,11 @@ bool CPrivateSendQueue::Relay(CConnman& connman)
return true;
}
bool CPrivateSendQueue::IsTimeOutOfBounds() const
{
return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT || nTime - GetAdjustedTime() > PRIVATESEND_QUEUE_TIMEOUT;
}
uint256 CPrivateSendBroadcastTx::GetSignatureHash() const
{
return SerializeHash(*this);
@ -174,8 +179,8 @@ void CPrivateSendBaseManager::CheckQueue()
// check mixing queue objects for timeouts
auto it = vecPrivateSendQueue.begin();
while (it != vecPrivateSendQueue.end()) {
if ((*it).IsExpired()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseManager::%s -- Removing expired queue (%s)\n", __func__, (*it).ToString());
if ((*it).IsTimeOutOfBounds()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseManager::%s -- Removing a queue (%s)\n", __func__, (*it).ToString());
it = vecPrivateSendQueue.erase(it);
} else {
++it;
@ -190,7 +195,7 @@ bool CPrivateSendBaseManager::GetQueueItemAndTry(CPrivateSendQueue& dsqRet)
for (auto& dsq : vecPrivateSendQueue) {
// only try each queue once
if (dsq.fTried || dsq.IsExpired()) continue;
if (dsq.fTried || dsq.IsTimeOutOfBounds()) continue;
dsq.fTried = true;
dsqRet = dsq;
return true;

View File

@ -271,8 +271,8 @@ public:
bool Relay(CConnman& connman);
/// Is this queue expired?
bool IsExpired() { return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT; }
/// Check if a queue is too old or too far into the future
bool IsTimeOutOfBounds() const;
std::string ToString() const
{