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

View File

@ -15,9 +15,6 @@
#include <map>
#include <boost/range/adaptors.hpp>
#include <boost/range/any_range.hpp>
class CBlock;
class CBlockIndex;
class CValidationState;
@ -220,41 +217,18 @@ public:
public:
size_t size() const
size_t GetAllMNsCount() const
{
return mnMap.size();
}
typedef boost::any_range<const CDeterministicMNCPtr&, boost::forward_traversal_tag> range_type;
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;
template<typename Callback>
void ForEachMN(bool onlyValid, Callback&& cb) const {
for (const auto& p : mnMap) {
if (IsMNValid(p.second)) {
c++;
if (!onlyValid || IsMNValid(p.second)) {
cb(p.second);
}
}
return c;
}
public:

View File

@ -56,11 +56,11 @@ CSimplifiedMNList::CSimplifiedMNList(const std::vector<CSimplifiedMNListEntry>&
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);
}
});
std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) {
return a.proRegTxHash.Compare(b.proRegTxHash) < 0;

View File

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

View File

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

View File

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

View File

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