Don't use boost range adaptors in CDeterministicMNList (#2327)

These cause crashes when used in for loops. Reason is very likely that
temporary CDeterministicMNList objects are destroyed before entering the
loop while the adaptors rely on these objects.

Discovered in one of our devnets while calling "protx list"
This commit is contained in:
Alexander Block 2018-10-02 11:03:05 +02:00 committed by UdjinM6
parent 07208a4ae2
commit eaef902025
7 changed files with 53 additions and 81 deletions

View File

@ -174,10 +174,11 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const
return nullptr; return nullptr;
CDeterministicMNCPtr best; CDeterministicMNCPtr best;
for (const auto& dmn : valid_range()) { ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
if (!best || CompareByLastPaid(dmn, best)) if (!best || CompareByLastPaid(dmn, best)) {
best = dmn; best = dmn;
} }
});
return best; return best;
} }
@ -310,8 +311,8 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde
evoDb.Write(std::make_pair(DB_LIST_DIFF, diff.blockHash), diff); evoDb.Write(std::make_pair(DB_LIST_DIFF, diff.blockHash), diff);
if ((nHeight % SNAPSHOT_LIST_PERIOD) == 0) { if ((nHeight % SNAPSHOT_LIST_PERIOD) == 0) {
evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, diff.blockHash), newList); evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, diff.blockHash), newList);
LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.size=%d\n", LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d\n",
__func__, nHeight, newList.size()); __func__, nHeight, newList.GetAllMNsCount());
} }
if (nHeight == GetSpork15Value()) { if (nHeight == GetSpork15Value()) {
@ -373,8 +374,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
if (dmn && dmn->nCollateralIndex == in.prevout.n) { if (dmn && dmn->nCollateralIndex == in.prevout.n) {
newList.RemoveMN(proTxHash); newList.RemoveMN(proTxHash);
LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. nHeight=%d, mapCurMNs.size=%d\n", LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. nHeight=%d, mapCurMNs.allMNsCount=%d\n",
__func__, proTxHash.ToString(), nHeight, newList.size()); __func__, proTxHash.ToString(), nHeight, newList.GetAllMNsCount());
} }
} }

View File

@ -15,9 +15,6 @@
#include <map> #include <map>
#include <boost/range/adaptors.hpp>
#include <boost/range/any_range.hpp>
class CBlock; class CBlock;
class CBlockIndex; class CBlockIndex;
class CValidationState; class CValidationState;
@ -220,41 +217,18 @@ public:
public: public:
size_t size() const size_t GetAllMNsCount() const
{ {
return mnMap.size(); return mnMap.size();
} }
typedef boost::any_range<const CDeterministicMNCPtr&, boost::forward_traversal_tag> range_type; template<typename Callback>
void ForEachMN(bool onlyValid, Callback&& cb) const {
range_type all_range() const
{
return boost::adaptors::transform(mnMap, [] (const MnMap::value_type& p) -> const CDeterministicMNCPtr& {
return p.second;
});
}
range_type valid_range() const
{
return boost::adaptors::filter(all_range(), [&] (const CDeterministicMNCPtr& dmn) -> bool {
return IsMNValid(dmn);
});
}
size_t all_count() const
{
return mnMap.size();
}
size_t valid_count() const
{
size_t c = 0;
for (const auto& p : mnMap) { for (const auto& p : mnMap) {
if (IsMNValid(p.second)) { if (!onlyValid || IsMNValid(p.second)) {
c++; cb(p.second);
} }
} }
return c;
} }
public: public:

View File

@ -56,11 +56,11 @@ CSimplifiedMNList::CSimplifiedMNList(const std::vector<CSimplifiedMNListEntry>&
CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList) CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList)
{ {
mnList.reserve(dmnList.all_count()); mnList.reserve(dmnList.GetAllMNsCount());
for (const auto& dmn : dmnList.all_range()) { dmnList.ForEachMN(false, [this](const CDeterministicMNCPtr& dmn) {
mnList.emplace_back(*dmn); mnList.emplace_back(*dmn);
} });
std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) { std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) {
return a.proRegTxHash.Compare(b.proRegTxHash) < 0; return a.proRegTxHash.Compare(b.proRegTxHash) < 0;

View File

@ -381,7 +381,7 @@ void CMasternodeMan::AddDeterministicMasternodes()
unsigned int oldMnCount = mapMasternodes.size(); unsigned int oldMnCount = mapMasternodes.size();
auto mnList = deterministicMNManager->GetListAtChainTip(); auto mnList = deterministicMNManager->GetListAtChainTip();
for (const auto& dmn : mnList.valid_range()) { mnList.ForEachMN(true, [this](const CDeterministicMNCPtr& dmn) {
// call Find() on each deterministic MN to force creation of CMasternode object // call Find() on each deterministic MN to force creation of CMasternode object
auto mn = Find(COutPoint(dmn->proTxHash, dmn->nCollateralIndex)); auto mn = Find(COutPoint(dmn->proTxHash, dmn->nCollateralIndex));
assert(mn); assert(mn);
@ -395,7 +395,7 @@ void CMasternodeMan::AddDeterministicMasternodes()
// If it appeared in the valid list, it is enabled no matter what // If it appeared in the valid list, it is enabled no matter what
mn->nActiveState = CMasternode::MASTERNODE_ENABLED; mn->nActiveState = CMasternode::MASTERNODE_ENABLED;
} });
added = oldMnCount != mapMasternodes.size(); added = oldMnCount != mapMasternodes.size();
} }
@ -415,9 +415,9 @@ void CMasternodeMan::RemoveNonDeterministicMasternodes()
LOCK(cs); LOCK(cs);
std::set<COutPoint> mnSet; std::set<COutPoint> mnSet;
auto mnList = deterministicMNManager->GetListAtChainTip(); auto mnList = deterministicMNManager->GetListAtChainTip();
for (const auto& dmn : mnList.valid_range()) { mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
mnSet.insert(COutPoint(dmn->proTxHash, dmn->nCollateralIndex)); mnSet.insert(COutPoint(dmn->proTxHash, dmn->nCollateralIndex));
} });
auto it = mapMasternodes.begin(); auto it = mapMasternodes.begin();
while (it != mapMasternodes.end()) { while (it != mapMasternodes.end()) {
if (!mnSet.count(it->second.outpoint)) { if (!mnSet.count(it->second.outpoint)) {
@ -455,10 +455,11 @@ int CMasternodeMan::CountMasternodes(int nProtocolVersion)
if (deterministicMNManager->IsDeterministicMNsSporkActive()) { if (deterministicMNManager->IsDeterministicMNsSporkActive()) {
auto mnList = deterministicMNManager->GetListAtChainTip(); auto mnList = deterministicMNManager->GetListAtChainTip();
for (const auto& dmn : mnList.valid_range()) { mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
if (dmn->pdmnState->nProtocolVersion < nProtocolVersion) continue; if (dmn->pdmnState->nProtocolVersion >= nProtocolVersion) {
nCount++; nCount++;
} }
});
} else { } else {
for (const auto& mnpair : mapMasternodes) { for (const auto& mnpair : mapMasternodes) {
if(mnpair.second.nProtocolVersion < nProtocolVersion) continue; if(mnpair.second.nProtocolVersion < nProtocolVersion) continue;
@ -1693,7 +1694,7 @@ std::string CMasternodeMan::ToString() const
if (deterministicMNManager->IsDeterministicMNsSporkActive()) { if (deterministicMNManager->IsDeterministicMNsSporkActive()) {
info << "Masternodes: masternode object count: " << (int)mapMasternodes.size() << info << "Masternodes: masternode object count: " << (int)mapMasternodes.size() <<
", deterministic masternode count: " << deterministicMNManager->GetListAtChainTip().size() << ", deterministic masternode count: " << deterministicMNManager->GetListAtChainTip().GetAllMNsCount() <<
", nDsqCount: " << (int)nDsqCount; ", nDsqCount: " << (int)nDsqCount;
} else { } else {
info << "Masternodes: " << (int)mapMasternodes.size() << info << "Masternodes: " << (int)mapMasternodes.size() <<

View File

@ -579,7 +579,7 @@ UniValue gobject_vote_many(const JSONRPCRequest& request)
// management // management
if (deterministicMNManager->IsDeterministicMNsSporkActive()) { if (deterministicMNManager->IsDeterministicMNsSporkActive()) {
auto mnList = deterministicMNManager->GetListAtChainTip(); auto mnList = deterministicMNManager->GetListAtChainTip();
for (const auto &dmn : mnList.valid_range()) { mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
bool found = false; bool found = false;
for (const auto &mne : entries) { for (const auto &mne : entries) {
uint256 nTxHash; uint256 nTxHash;
@ -595,10 +595,7 @@ UniValue gobject_vote_many(const JSONRPCRequest& request)
break; break;
} }
} }
if (found) { if (!found) {
continue;
}
CKey ownerKey; CKey ownerKey;
if (pwalletMain->GetKey(dmn->pdmnState->keyIDVoting, ownerKey)) { if (pwalletMain->GetKey(dmn->pdmnState->keyIDVoting, ownerKey)) {
CBitcoinSecret secret(ownerKey); CBitcoinSecret secret(ownerKey);
@ -606,6 +603,7 @@ UniValue gobject_vote_many(const JSONRPCRequest& request)
entries.push_back(mne); entries.push_back(mne);
} }
} }
});
} }
#endif #endif

View File

@ -519,7 +519,7 @@ UniValue protx_list(const JSONRPCRequest& request)
setOutpts.emplace(outpt.hash); setOutpts.emplace(outpt.hash);
} }
for (const auto& dmn : deterministicMNManager->GetListAtChainTip().all_range()) { deterministicMNManager->GetListAtChainTip().ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) {
if (setOutpts.count(dmn->proTxHash) || if (setOutpts.count(dmn->proTxHash) ||
pwalletMain->HaveKey(dmn->pdmnState->keyIDOwner) || pwalletMain->HaveKey(dmn->pdmnState->keyIDOwner) ||
pwalletMain->HaveKey(dmn->pdmnState->keyIDOperator) || pwalletMain->HaveKey(dmn->pdmnState->keyIDOperator) ||
@ -528,7 +528,7 @@ UniValue protx_list(const JSONRPCRequest& request)
CheckWalletOwnsScript(dmn->pdmnState->scriptOperatorPayout)) { CheckWalletOwnsScript(dmn->pdmnState->scriptOperatorPayout)) {
ret.push_back(BuildDMNListEntry(dmn, detailed)); ret.push_back(BuildDMNListEntry(dmn, detailed));
} }
} });
} else if (type == "valid" || type == "registered") { } else if (type == "valid" || type == "registered") {
if (request.params.size() > 4) if (request.params.size() > 4)
protx_list_help(); protx_list_help();
@ -542,16 +542,10 @@ UniValue protx_list(const JSONRPCRequest& request)
bool detailed = request.params.size() > 3 ? ParseBoolV(request.params[3], "detailed") : false; bool detailed = request.params.size() > 3 ? ParseBoolV(request.params[3], "detailed") : false;
CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(chainActive[height]->GetBlockHash()); CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(chainActive[height]->GetBlockHash());
CDeterministicMNList::range_type range; bool onlyValid = type == "valid";
mnList.ForEachMN(onlyValid, [&](const CDeterministicMNCPtr& dmn) {
if (type == "valid") {
range = mnList.valid_range();
} else if (type == "registered") {
range = mnList.all_range();
}
for (const auto& dmn : range) {
ret.push_back(BuildDMNListEntry(dmn, detailed)); ret.push_back(BuildDMNListEntry(dmn, detailed));
} });
} else { } else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid type specified"); throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid type specified");
} }

View File

@ -199,11 +199,15 @@ static CDeterministicMNCPtr FindPayoutDmn(const CBlock& block)
{ {
auto dmnList = deterministicMNManager->GetListAtChainTip(); auto dmnList = deterministicMNManager->GetListAtChainTip();
for (auto txout : block.vtx[0]->vout) { for (const auto& txout : block.vtx[0]->vout) {
for (auto& dmn : dmnList.valid_range()) { CDeterministicMNCPtr found;
if (txout.scriptPubKey == dmn->pdmnState->scriptPayout) { dmnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
return dmn; if (found == nullptr && txout.scriptPubKey == dmn->pdmnState->scriptPayout) {
found = dmn;
} }
});
if (found != nullptr) {
return found;
} }
} }
return nullptr; return nullptr;