diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 12baa3cfcec96..225cd5752e8ff 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -214,38 +214,64 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex) return best; } -std::vector CDeterministicMNList::GetProjectedMNPayeesAtChainTip(int nCount) const -{ - return GetProjectedMNPayees(::ChainActive()[nHeight], nCount); -} - -std::vector CDeterministicMNList::GetProjectedMNPayees(const CBlockIndex* const pindex, int nCount) const +std::vector CDeterministicMNList::GetProjectedMNPayees(int nCount) const { if (nCount < 0 ) { return {}; } - nCount = std::min(nCount, int(GetValidWeightedMNsCount())); + + bool isMNRewardReallocation{false}; + if (auto pindex = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(blockHash)); pindex == nullptr) { + // Something went wrong, probably a race condition in tip and mnlist updates, + // try recovering... + pindex = ::ChainActive().Tip(); + const auto mn_rr_state = llmq::utils::GetMNRewardReallocationState(pindex); + isMNRewardReallocation = (mn_rr_state == ThresholdState::ACTIVE); + if (!isMNRewardReallocation) { + const auto w_size = static_cast(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize); + // Check if mn_rr is going to be active at nHeight + if (mn_rr_state == ThresholdState::LOCKED_IN) { + int activation_height = llmq::utils::GetMNRewardReallocationSince(pindex) + w_size; + if (nHeight >= activation_height) { + isMNRewardReallocation = true; + } + } else { + if (nHeight - pindex->nHeight > w_size) { + // Should never happen, can't do anything + return {}; + } + // No way we reach mn_rr activation if it's not locked in yet + // and height diff is less than or equal w_size, so isMNRewardReallocation == false + // but it's safe to continue nevertheless. + } + } + } else { + isMNRewardReallocation = llmq::utils::IsMNRewardReallocationActive(pindex); + } + + const auto weighted_count = GetValidWeightedMNsCount(); + nCount = std::min(nCount, int(weighted_count)); std::vector result; - result.reserve(nCount); + result.reserve(weighted_count); auto remaining_evo_payments = 0; CDeterministicMNCPtr evo_to_be_skipped = nullptr; - bool isMNRewardReallocation = llmq::utils::IsMNRewardReallocationActive(pindex); - ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { - if (dmn->pdmnState->nLastPaidHeight == nHeight) { - // We found the last MN Payee. - // If the last payee is an EvoNode, we need to check its consecutive payments and pay him again if needed - if (!isMNRewardReallocation && dmn->nType == MnType::Evo && dmn->pdmnState->nConsecutivePayments < dmn_types::Evo.voting_weight) { - remaining_evo_payments = dmn_types::Evo.voting_weight - dmn->pdmnState->nConsecutivePayments; - for ([[maybe_unused]] auto _ : irange::range(remaining_evo_payments)) { - result.emplace_back(dmn); - evo_to_be_skipped = dmn; + if (!isMNRewardReallocation) { + ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->nLastPaidHeight == nHeight) { + // We found the last MN Payee. + // If the last payee is an EvoNode, we need to check its consecutive payments and pay him again if needed + if (dmn->nType == MnType::Evo && dmn->pdmnState->nConsecutivePayments < dmn_types::Evo.voting_weight) { + remaining_evo_payments = dmn_types::Evo.voting_weight - dmn->pdmnState->nConsecutivePayments; + for ([[maybe_unused]] auto _ : irange::range(remaining_evo_payments)) { + result.emplace_back(dmn); + evo_to_be_skipped = dmn; + } } } - } - return; - }); + }); + } ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { if (dmn == evo_to_be_skipped) return; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 8af688c822897..87ac8c57fc4e2 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -347,8 +347,7 @@ class CDeterministicMNList * @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetValidWeightedMNsCount twice. * @return */ - [[nodiscard]] std::vector GetProjectedMNPayees(const CBlockIndex* const pindex, int nCount = std::numeric_limits::max()) const; - [[nodiscard]] std::vector GetProjectedMNPayeesAtChainTip(int nCount = std::numeric_limits::max()) const; + [[nodiscard]] std::vector GetProjectedMNPayees(int nCount = std::numeric_limits::max()) const; /** * Calculate a quorum based on the modifier. The resulting list is deterministically sorted by score diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 47de1cd86bf1c..0ae18e4253e11 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -676,7 +676,7 @@ void CGovernanceManager::CreateGovernanceTrigger(const CSuperblock& sb, CConnman // Nobody submitted a trigger we'd like to see, // so let's do it but only if we are the payee auto mnList = deterministicMNManager->GetListAtChainTip(); - auto mn_payees = mnList.GetProjectedMNPayeesAtChainTip(); + auto mn_payees = mnList.GetProjectedMNPayees(); if (mn_payees.empty()) return; { LOCK(activeMasternodeInfoCs); diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index dccd5e15ff095..de55be793e1e3 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -168,6 +168,14 @@ void MasternodeList::updateDIP3List() } auto mnList = clientModel->getMasternodeList(); + auto projectedPayees = mnList.GetProjectedMNPayees(); + + if (projectedPayees.empty() && mnList.GetValidMNsCount() > 0) { + // GetProjectedMNPayees failed to provide results for a list with valid mns. + // Keep current list and let it try again later. + return; + } + std::map mapCollateralDests; { @@ -192,7 +200,6 @@ void MasternodeList::updateDIP3List() nTimeUpdatedDIP3 = GetTime(); - auto projectedPayees = mnList.GetProjectedMNPayeesAtChainTip(); std::map nextPayments; for (size_t i = 0; i < projectedPayees.size(); i++) { const auto& dmn = projectedPayees[i]; diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index d3182caa22312..26d2ae125781b 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -136,7 +136,7 @@ static UniValue masternode_count(const JSONRPCRequest& request) static UniValue GetNextMasternodeForPayment(int heightShift) { auto mnList = deterministicMNManager->GetListAtChainTip(); - auto payees = mnList.GetProjectedMNPayeesAtChainTip(heightShift); + auto payees = mnList.GetProjectedMNPayees(heightShift); if (payees.empty()) return "unknown"; auto payee = payees.back(); @@ -360,7 +360,7 @@ static UniValue masternode_winners(const JSONRPCRequest& request, const Chainsta obj.pushKV(strprintf("%d", h), strPayments); } - auto projection = deterministicMNManager->GetListForBlock(pindexTip).GetProjectedMNPayees(pindexTip, 20); + auto projection = deterministicMNManager->GetListForBlock(pindexTip).GetProjectedMNPayees(20); for (size_t i = 0; i < projection.size(); i++) { int h = nChainTipHeight + 1 + i; std::string strPayments = GetRequiredPaymentsString(h, projection[i]);