diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index ea789de75b59..2c1f9c1346ef 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -265,58 +265,6 @@ std::vector CDeterministicMNList::GetProjectedMNPayees(gsl return result; } -std::vector CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier, const bool onlyEvoNodes) const -{ - auto scores = CalculateScores(modifier, onlyEvoNodes); - - // sort is descending order - std::sort(scores.rbegin(), scores.rend(), [](const std::pair& a, const std::pair& b) { - if (a.first == b.first) { - // this should actually never happen, but we should stay compatible with how the non-deterministic MNs did the sorting - return a.second->collateralOutpoint < b.second->collateralOutpoint; - } - return a.first < b.first; - }); - - // take top maxSize entries and return it - std::vector result; - result.resize(std::min(maxSize, scores.size())); - for (size_t i = 0; i < result.size(); i++) { - result[i] = std::move(scores[i].second); - } - return result; -} - -std::vector> CDeterministicMNList::CalculateScores(const uint256& modifier, const bool onlyEvoNodes) const -{ - std::vector> scores; - scores.reserve(GetAllMNsCount()); - ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { - if (dmn->pdmnState->confirmedHash.IsNull()) { - // we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a - // future quorums - return; - } - if (onlyEvoNodes) { - if (dmn->nType != MnType::Evo) - return; - } - // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN - // Please note that this is not a double-sha256 but a single-sha256 - // The first part is already precalculated (confirmedHashWithProRegTxHash) - // TODO When https://github.com/bitcoin/bitcoin/pull/13191 gets backported, implement something that is similar but for single-sha256 - uint256 h; - CSHA256 sha256; - sha256.Write(dmn->pdmnState->confirmedHashWithProRegTxHash.begin(), dmn->pdmnState->confirmedHashWithProRegTxHash.size()); - sha256.Write(modifier.begin(), modifier.size()); - sha256.Finalize(h.begin()); - - scores.emplace_back(UintToArith256(h), dmn); - }); - - return scores; -} - int CDeterministicMNList::CalcMaxPoSePenalty() const { // Maximum PoSe penalty is dynamic and equals the number of registered MNs diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 386d98a23063..05a054a44af5 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -332,12 +332,6 @@ class CDeterministicMNList */ [[nodiscard]] std::vector GetProjectedMNPayees(gsl::not_null pindexPrev, int nCount = std::numeric_limits::max()) const; - /** - * Calculate a quorum based on the modifier. The resulting list is deterministically sorted by score - */ - [[nodiscard]] std::vector CalculateQuorum(size_t maxSize, const uint256& modifier, const bool onlyEvoNodes = false) const; - [[nodiscard]] std::vector> CalculateScores(const uint256& modifier, const bool onlyEvoNodes) const; - /** * Calculates the maximum penalty which is allowed at the height of this MN list. It is dynamic and might change * for every block. diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 8966a3e6c05f..020288196eee 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -75,7 +75,7 @@ static PreviousQuorumQuarters GetPreviousQuorumQuarterMembers(const Consensus::L static std::vector> GetQuorumQuarterMembersBySnapshot( const Consensus::LLMQParams& llmqParams, CDeterministicMNManager& dmnman, const CBlockIndex* pCycleQuorumBaseBlockIndex, const llmq::CQuorumSnapshot& snapshot, int nHeight); -static std::pair GetMNUsageBySnapshot( +static std::pair, std::vector> GetMNUsageBySnapshot( const Consensus::LLMQParams& llmqParams, CDeterministicMNManager& dmnman, const CBlockIndex* pCycleQuorumBaseBlockIndex, const llmq::CQuorumSnapshot& snapshot, int nHeight); @@ -108,6 +108,98 @@ static uint256 GetHashModifier(const Consensus::LLMQParams& llmqParams, gsl::not return ::SerializeHash(std::make_pair(llmqParams.type, pCycleQuorumBaseBlockIndex->GetBlockHash())); } +static arith_uint256 calculateQuorumScore(const CDeterministicMNCPtr& dmn, const uint256& modifier) +{ + // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN + // Please note that this is not a double-sha256 but a single-sha256 + // The first part is already precalculated (confirmedHashWithProRegTxHash) + // TODO When https://github.com/bitcoin/bitcoin/pull/13191 gets backported, implement something that is similar but for single-sha256 + uint256 h; + CSHA256 sha256; + sha256.Write(dmn->pdmnState->confirmedHashWithProRegTxHash.begin(), + dmn->pdmnState->confirmedHashWithProRegTxHash.size()); + sha256.Write(modifier.begin(), modifier.size()); + sha256.Finalize(h.begin()); + return UintToArith256(h); +} + +static std::vector> CalculateScoresForQuorum( + std::vector&& dmns, const uint256& modifier, const bool onlyEvoNodes) +{ + std::vector> scores; + scores.reserve(dmns.size()); + + for (auto& dmn : dmns) { + if (dmn->pdmnState->IsBanned()) continue; + if (dmn->pdmnState->confirmedHash.IsNull()) { + // we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a + // future quorums + continue; + } + if (onlyEvoNodes && dmn->nType != MnType::Evo) { + continue; + } + scores.emplace_back(calculateQuorumScore(dmn, modifier), std::move(dmn)); + }; + return scores; +} + +static std::vector> CalculateScoresForQuorum( + const CDeterministicMNList& mn_list, const uint256& modifier, const bool onlyEvoNodes) +{ + std::vector> scores; + scores.reserve(mn_list.GetAllMNsCount()); + + mn_list.ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->confirmedHash.IsNull()) { + // we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a + // future quorums + return; + } + if (onlyEvoNodes && dmn->nType != MnType::Evo) { + return; + } + + scores.emplace_back(calculateQuorumScore(dmn, modifier), dmn); + }); + return scores; +} + + +/** + * Calculate a quorum based on the modifier. The resulting list is deterministically sorted by score + */ +template +static std::vector CalculateQuorum(List&& mn_list, const uint256& modifier, size_t maxSize = 0, + const bool onlyEvoNodes = false) +{ + auto scores = CalculateScoresForQuorum(std::forward(mn_list), modifier, onlyEvoNodes); + + // sort is descending order + std::sort(scores.rbegin(), scores.rend(), + [](const std::pair& a, + const std::pair& b) { + if (a.first == b.first) { + // this should actually never happen, but we should stay compatible with how the non-deterministic MNs did the sorting + // TODO - add assert ? + return a.second->collateralOutpoint < b.second->collateralOutpoint; + } + return a.first < b.first; + }); + + // return top maxSize entries only (if specified) + if (maxSize > 0 && scores.size() > maxSize) { + scores.resize(maxSize); + } + + std::vector result; + result.reserve(scores.size()); + for (auto& score : scores) { + result.emplace_back(std::move(score.second)); + } + return result; +} + std::vector GetAllQuorumMembers(Consensus::LLMQType llmqType, CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, gsl::not_null pQuorumBaseBlockIndex, @@ -171,12 +263,13 @@ std::vector GetAllQuorumMembers(Consensus::LLMQType llmqTy } auto q = ComputeQuorumMembersByQuarterRotation(llmq_params, dmnman, qsnapman, pCycleQuorumBaseBlockIndex); + quorumMembers = q[quorumIndex]; + LOCK(cs_indexed_members); for (const size_t i : irange::range(q.size())) { - mapIndexedQuorumMembers[llmqType].insert(std::make_pair(pCycleQuorumBaseBlockIndex->GetBlockHash(), i), q[i]); + mapIndexedQuorumMembers[llmqType].emplace(std::make_pair(pCycleQuorumBaseBlockIndex->GetBlockHash(), i), + std::move(q[i])); } - - quorumMembers = q[quorumIndex]; } else { quorumMembers = ComputeQuorumMembers(llmqType, dmnman, pQuorumBaseBlockIndex); } @@ -202,7 +295,7 @@ std::vector ComputeQuorumMembers(Consensus::LLMQType llmqT pQuorumBaseBlockIndex; const auto modifier = GetHashModifier(llmq_params_opt.value(), pQuorumBaseBlockIndex); auto allMns = dmnman.GetListForBlock(pWorkBlockIndex); - return allMns.CalculateQuorum(llmq_params_opt->size, modifier, EvoOnly); + return CalculateQuorum(allMns, modifier, llmq_params_opt->size, EvoOnly); } std::vector> ComputeQuorumMembersByQuarterRotation( @@ -345,7 +438,6 @@ std::vector> BuildNewQuorumQuarterMembers( } auto MnsUsedAtH = CDeterministicMNList(); - auto MnsNotUsedAtH = CDeterministicMNList(); std::vector MnsUsedAtHIndexed{nQuorums}; bool skipRemovedMNs = IsV19Active(pCycleQuorumBaseBlockIndex) || (Params().NetworkIDString() == CBaseChainParams::TESTNET); @@ -401,20 +493,17 @@ std::vector> BuildNewQuorumQuarterMembers( } } + std::vector MnsNotUsedAtH; allMns.ForEachMNShared(false, [&MnsUsedAtH, &MnsNotUsedAtH](const CDeterministicMNCPtr& dmn) { if (!MnsUsedAtH.HasMN(dmn->proTxHash)) { if (!dmn->pdmnState->IsBanned()) { - try { - MnsNotUsedAtH.AddMN(dmn); - } catch (const std::runtime_error& e) { - } + MnsNotUsedAtH.push_back(dmn); } } }); - auto sortedMnsUsedAtHM = MnsUsedAtH.CalculateQuorum(MnsUsedAtH.GetAllMNsCount(), modifier); - auto sortedMnsNotUsedAtH = MnsNotUsedAtH.CalculateQuorum(MnsNotUsedAtH.GetAllMNsCount(), modifier); - auto sortedCombinedMnsList = std::move(sortedMnsNotUsedAtH); + auto sortedMnsUsedAtHM = CalculateQuorum(MnsUsedAtH, modifier); + auto sortedCombinedMnsList = CalculateQuorum(std::move(MnsNotUsedAtH), modifier); for (auto& m : sortedMnsUsedAtHM) { sortedCombinedMnsList.push_back(std::move(m)); } @@ -493,7 +582,7 @@ void BuildQuorumSnapshot(const Consensus::LLMQParams& llmqParams, const CDetermi quorumSnapshot.activeQuorumMembers.resize(allMns.GetAllMNsCount()); const auto modifier = GetHashModifier(llmqParams, pCycleQuorumBaseBlockIndex); - auto sortedAllMns = allMns.CalculateQuorum(allMns.GetAllMNsCount(), modifier); + auto sortedAllMns = CalculateQuorum(allMns, modifier); LogPrint(BCLog::LLMQ, "BuildQuorumSnapshot h[%d] numMns[%d]\n", pCycleQuorumBaseBlockIndex->nHeight, allMns.GetAllMNsCount()); @@ -529,12 +618,12 @@ std::vector> GetQuorumQuarterMembersBySnapshot std::vector sortedCombinedMns; { const auto modifier = GetHashModifier(llmqParams, pCycleQuorumBaseBlockIndex); - const auto [MnsUsedAtH, MnsNotUsedAtH] = GetMNUsageBySnapshot(llmqParams, dmnman, pCycleQuorumBaseBlockIndex, snapshot, nHeight); + auto [MnsUsedAtH, MnsNotUsedAtH] = GetMNUsageBySnapshot(llmqParams, dmnman, pCycleQuorumBaseBlockIndex, + snapshot, nHeight); // the list begins with all the unused MNs - auto sortedMnsNotUsedAtH = MnsNotUsedAtH.CalculateQuorum(MnsNotUsedAtH.GetAllMNsCount(), modifier); - sortedCombinedMns = std::move(sortedMnsNotUsedAtH); + sortedCombinedMns = CalculateQuorum(std::move(MnsNotUsedAtH), modifier); // Now add the already used MNs to the end of the list - auto sortedMnsUsedAtH = MnsUsedAtH.CalculateQuorum(MnsUsedAtH.GetAllMNsCount(), modifier); + auto sortedMnsUsedAtH = CalculateQuorum(std::move(MnsUsedAtH), modifier); std::move(sortedMnsUsedAtH.begin(), sortedMnsUsedAtH.end(), std::back_inserter(sortedCombinedMns)); } @@ -611,45 +700,37 @@ std::vector> GetQuorumQuarterMembersBySnapshot } } -std::pair GetMNUsageBySnapshot(const Consensus::LLMQParams& llmqParams, - CDeterministicMNManager& dmnman, - const CBlockIndex* pCycleQuorumBaseBlockIndex, - const llmq::CQuorumSnapshot& snapshot, - int nHeight) +static std::pair, std::vector> GetMNUsageBySnapshot( + const Consensus::LLMQParams& llmqParams, CDeterministicMNManager& dmnman, + const CBlockIndex* pCycleQuorumBaseBlockIndex, const llmq::CQuorumSnapshot& snapshot, int nHeight) { if (!llmqParams.useRotation || pCycleQuorumBaseBlockIndex->nHeight % llmqParams.dkgInterval != 0) { ASSERT_IF_DEBUG(false); return {}; } - CDeterministicMNList usedMNs; - CDeterministicMNList nonUsedMNs; + std::vector usedMNs; + std::vector nonUsedMNs; const CBlockIndex* pWorkBlockIndex = pCycleQuorumBaseBlockIndex->GetAncestor(pCycleQuorumBaseBlockIndex->nHeight - 8); const auto modifier = GetHashModifier(llmqParams, pCycleQuorumBaseBlockIndex); auto allMns = dmnman.GetListForBlock(pWorkBlockIndex); - auto sortedAllMns = allMns.CalculateQuorum(allMns.GetAllMNsCount(), modifier); + auto sortedAllMns = CalculateQuorum(allMns, modifier); size_t i{0}; for (const auto& dmn : sortedAllMns) { if (snapshot.activeQuorumMembers[i]) { - try { - usedMNs.AddMN(dmn); - } catch (const std::runtime_error& e) { - } + usedMNs.push_back(dmn); } else { if (!dmn->pdmnState->IsBanned()) { - try { - nonUsedMNs.AddMN(dmn); - } catch (const std::runtime_error& e) { - } + nonUsedMNs.push_back(dmn); } } i++; } - return std::make_pair(usedMNs, nonUsedMNs); + return {usedMNs, nonUsedMNs}; } uint256 DeterministicOutboundConnection(const uint256& proTxHash1, const uint256& proTxHash2)