Skip to content

Commit 5da28cc

Browse files
committed
[BUG] Fix potential deadlock
CountEnabled is being called while holding CMasternodeMan::cs, by SecondsSincePayment, and it locks CDeterministicMNManager::cs to get the deterministic manager's tip height. Pass the enabled masternode count to SecondsSincePayment, directly from the caller, so the two locks are not held at the same time.
1 parent 8cc9735 commit 5da28cc

File tree

3 files changed

+23
-19
lines changed

3 files changed

+23
-19
lines changed

src/masternodeman.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,13 @@ int CMasternodeMan::CountEnabled(bool only_legacy) const
420420
int i = 0;
421421
int protocolVersion = ActiveProtocol();
422422

423-
for (const auto& it : mapMasternodes) {
424-
const MasternodeRef& mn = it.second;
425-
if (mn->protocolVersion < protocolVersion || !mn->IsEnabled()) continue;
426-
i++;
423+
{
424+
LOCK(cs);
425+
for (const auto& it : mapMasternodes) {
426+
const MasternodeRef& mn = it.second;
427+
if (mn->protocolVersion < protocolVersion || !mn->IsEnabled()) continue;
428+
i++;
429+
}
427430
}
428431

429432
if (!only_legacy && deterministicMNManager->IsDIP3Enforced()) {
@@ -543,13 +546,13 @@ MasternodeRef CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeigh
543546
Make a vector with all of the last paid times
544547
*/
545548
int minProtocol = ActiveProtocol();
546-
int nMnCount = CountEnabled();
549+
int count_enabled = CountEnabled();
547550
{
548551
LOCK(cs);
549552
for (const auto& it : mapMasternodes) {
550553
if (!it.second->IsEnabled()) continue;
551-
if (canScheduleMN(fFilterSigTime, it.second, minProtocol, nMnCount, nBlockHeight)) {
552-
vecMasternodeLastPaid.emplace_back(SecondsSincePayment(it.second, BlockReading), it.second);
554+
if (canScheduleMN(fFilterSigTime, it.second, minProtocol, count_enabled, nBlockHeight)) {
555+
vecMasternodeLastPaid.emplace_back(SecondsSincePayment(it.second, count_enabled, BlockReading), it.second);
553556
}
554557
}
555558
}
@@ -558,16 +561,16 @@ MasternodeRef CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeigh
558561
CDeterministicMNList mnList = deterministicMNManager->GetListAtChainTip();
559562
mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
560563
const MasternodeRef mn = MakeMasternodeRefForDMN(dmn);
561-
if (canScheduleMN(fFilterSigTime, mn, minProtocol, nMnCount, nBlockHeight)) {
562-
vecMasternodeLastPaid.emplace_back(SecondsSincePayment(mn, BlockReading), mn);
564+
if (canScheduleMN(fFilterSigTime, mn, minProtocol, count_enabled, nBlockHeight)) {
565+
vecMasternodeLastPaid.emplace_back(SecondsSincePayment(mn, count_enabled, BlockReading), mn);
563566
}
564567
});
565568
}
566569

567570
nCount = (int)vecMasternodeLastPaid.size();
568571

569572
//when the network is in the process of upgrading, don't penalize nodes that recently restarted
570-
if (fFilterSigTime && nCount < nMnCount / 3) return GetNextMasternodeInQueueForPayment(nBlockHeight, false, nCount, BlockReading);
573+
if (fFilterSigTime && nCount < count_enabled / 3) return GetNextMasternodeInQueueForPayment(nBlockHeight, false, nCount, BlockReading);
571574

572575
// Sort them high to low
573576
sort(vecMasternodeLastPaid.rbegin(), vecMasternodeLastPaid.rend(), CompareScoreMN());
@@ -576,7 +579,7 @@ MasternodeRef CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeigh
576579
// -- This doesn't look at who is being paid in the +8-10 blocks, allowing for double payments very rarely
577580
// -- 1/100 payments should be a double payment on mainnet - (1/(3000/10))*2
578581
// -- (chance per block * chances before IsScheduled will fire)
579-
int nTenthNetwork = nMnCount / 10;
582+
int nTenthNetwork = count_enabled / 10;
580583
int nCountTenth = 0;
581584
arith_uint256 nHigh = ARITH_UINT256_ZERO;
582585
const uint256& hash = GetHashAtHeight(nBlockHeight - 101);
@@ -943,9 +946,9 @@ void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast& mnb)
943946
}
944947
}
945948

946-
int64_t CMasternodeMan::SecondsSincePayment(const MasternodeRef& mn, const CBlockIndex* BlockReading) const
949+
int64_t CMasternodeMan::SecondsSincePayment(const MasternodeRef& mn, int count_enabled, const CBlockIndex* BlockReading) const
947950
{
948-
int64_t sec = (GetAdjustedTime() - GetLastPaid(mn, BlockReading));
951+
int64_t sec = (GetAdjustedTime() - GetLastPaid(mn, count_enabled, BlockReading));
949952
int64_t month = 60 * 60 * 24 * 30;
950953
if (sec < month) return sec; //if it's less than 30 days, give seconds
951954

@@ -958,7 +961,7 @@ int64_t CMasternodeMan::SecondsSincePayment(const MasternodeRef& mn, const CBloc
958961
return month + hash.GetCompact(false);
959962
}
960963

961-
int64_t CMasternodeMan::GetLastPaid(const MasternodeRef& mn, const CBlockIndex* BlockReading) const
964+
int64_t CMasternodeMan::GetLastPaid(const MasternodeRef& mn, int count_enabled, const CBlockIndex* BlockReading) const
962965
{
963966
if (BlockReading == nullptr) return false;
964967

@@ -972,8 +975,8 @@ int64_t CMasternodeMan::GetLastPaid(const MasternodeRef& mn, const CBlockIndex*
972975
// use a deterministic offset to break a tie -- 2.5 minutes
973976
int64_t nOffset = UintToArith256(hash).GetCompact(false) % 150;
974977

975-
int nMnCount = CountEnabled() * 1.25;
976-
for (int n = 0; n < nMnCount; n++) {
978+
int max_depth = count_enabled * 1.25;
979+
for (int n = 0; n < max_depth; n++) {
977980
const auto& it = masternodePayments.mapMasternodeBlocks.find(BlockReading->nHeight);
978981
if (it != masternodePayments.mapMasternodeBlocks.end()) {
979982
// Search for this payee, with at least 2 votes. This will aid in consensus

src/masternodeman.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ class CMasternodeMan
181181
void UpdateMasternodeList(CMasternodeBroadcast& mnb);
182182

183183
/// Get the time a masternode was last paid
184-
int64_t GetLastPaid(const MasternodeRef& mn, const CBlockIndex* BlockReading) const;
185-
int64_t SecondsSincePayment(const MasternodeRef& mn, const CBlockIndex* BlockReading) const;
184+
int64_t GetLastPaid(const MasternodeRef& mn, int count_enabled, const CBlockIndex* BlockReading) const;
185+
int64_t SecondsSincePayment(const MasternodeRef& mn, int count_enabled, const CBlockIndex* BlockReading) const;
186186

187187
// Block hashes cycling vector management
188188
void CacheBlockHash(const CBlockIndex* pindex);

src/rpc/masternode.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ UniValue listmasternodes(const JSONRPCRequest& request)
191191
int nHeight = chainTip->nHeight;
192192
auto mnList = deterministicMNManager->GetListAtChainTip();
193193

194+
int count_enabled = mnodeman.CountEnabled();
194195
std::vector<std::pair<int64_t, MasternodeRef>> vMasternodeRanks = mnodeman.GetMasternodeRanks(nHeight);
195196
for (int pos=0; pos < (int) vMasternodeRanks.size(); pos++) {
196197
const auto& s = vMasternodeRanks[pos];
@@ -244,7 +245,7 @@ UniValue listmasternodes(const JSONRPCRequest& request)
244245
obj.pushKV("version", mn.protocolVersion);
245246
obj.pushKV("lastseen", (int64_t)mn.lastPing.sigTime);
246247
obj.pushKV("activetime", (int64_t)(mn.lastPing.sigTime - mn.sigTime));
247-
obj.pushKV("lastpaid", (int64_t)mnodeman.GetLastPaid(s.second, chainTip));
248+
obj.pushKV("lastpaid", (int64_t)mnodeman.GetLastPaid(s.second, count_enabled, chainTip));
248249

249250
ret.push_back(obj);
250251
}

0 commit comments

Comments
 (0)