diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml index 28975eae73bf..3ad1d1a4d93d 100644 --- a/.github/workflows/semantic-pull-request.yml +++ b/.github/workflows/semantic-pull-request.yml @@ -38,6 +38,7 @@ jobs: # Configure which scopes are allowed (newline delimited). scopes: | consensus + interfaces log mining net @@ -45,7 +46,7 @@ jobs: rest rpc scripts + stats utils wallet zmq - stats diff --git a/src/active/quorums.cpp b/src/active/quorums.cpp index 79c5c892846c..c6c82f219bef 100644 --- a/src/active/quorums.cpp +++ b/src/active/quorums.cpp @@ -89,7 +89,7 @@ size_t QuorumParticipant::GetQuorumRecoveryStartOffset(const CQuorum& quorum, gs { auto mns = m_dmnman.GetListForBlock(pIndex); std::vector vecProTxHashes; - vecProTxHashes.reserve(mns.GetValidMNsCount()); + vecProTxHashes.reserve(mns.GetCounts().enabled()); mns.ForEachMN(/*onlyValid=*/true, [&](const auto& pMasternode) { vecProTxHashes.emplace_back(pMasternode.proTxHash); }); std::sort(vecProTxHashes.begin(), vecProTxHashes.end()); diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index 8299cd9d86cb..be6e7b458115 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -124,7 +124,7 @@ MessageProcessingResult CCoinJoinClientQueueManager::ProcessMessage(NodeId from, LogPrint(BCLog::COINJOIN, "DSQUEUE -- CoinJoin queue is ready, masternode=%s, queue=%s\n", dmn->proTxHash.ToString(), dsq.ToString()); return ret; } else { - if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetValidMNsCount())) { + if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetCounts().enabled())) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- Masternode %s is sending too many dsq messages\n", dmn->proTxHash.ToString()); return ret; @@ -826,7 +826,7 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(ChainstateManager& chainman return false; } - if (m_dmnman.GetListAtChainTip().GetValidMNsCount() == 0 && + if (m_dmnman.GetListAtChainTip().GetCounts().enabled() == 0 && Params().NetworkIDString() != CBaseChainParams::REGTEST) { strAutoDenomResult = _("No Masternodes detected."); WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- %s\n", strAutoDenomResult.original); @@ -988,7 +988,7 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(ChainstateManager& chainman return false; } - int nMnCountEnabled = m_dmnman.GetListAtChainTip().GetValidMNsCount(); + int nMnCountEnabled = m_dmnman.GetListAtChainTip().GetCounts().enabled(); // If we've used 90% of the Masternode list then drop the oldest first ~30% int nThreshold_high = nMnCountEnabled * 0.9; @@ -1033,7 +1033,7 @@ CDeterministicMNCPtr CCoinJoinClientManager::GetRandomNotUsedMasternode() { auto mnList = m_dmnman.GetListAtChainTip(); - size_t nCountEnabled = mnList.GetValidMNsCount(); + size_t nCountEnabled = mnList.GetCounts().enabled(); size_t nCountNotExcluded{nCountEnabled - m_mn_metaman.GetUsedMasternodesCount()}; WalletCJLogPrint(m_wallet, "CCoinJoinClientManager::%s -- %d enabled masternodes, %d masternodes to choose from\n", __func__, nCountEnabled, nCountNotExcluded); @@ -1078,7 +1078,7 @@ bool CCoinJoinClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, if (m_queueman == nullptr) return false; const auto mnList = m_dmnman.GetListAtChainTip(); - const int nWeightedMnCount = mnList.GetValidWeightedMNsCount(); + const int nWeightedMnCount = mnList.GetCounts().m_valid_weighted; // Look through the queues and see if anything matches CCoinJoinQueue dsq; @@ -1143,8 +1143,9 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon int nTries = 0; const auto mnList = m_dmnman.GetListAtChainTip(); - const int nMnCount = mnList.GetValidMNsCount(); - const int nWeightedMnCount = mnList.GetValidWeightedMNsCount(); + const auto mnCounts = mnList.GetCounts(); + const int nMnCount = mnCounts.enabled(); + const int nWeightedMnCount = mnCounts.m_valid_weighted; // find available denominated amounts std::set setAmounts; diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index c68e33224a56..e431e1d9632b 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -100,7 +100,7 @@ void CCoinJoinServer::ProcessDSACCEPT(CNode& peer, CDataStream& vRecv) } } - if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, mnList.GetValidMNsCount())) { + if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, mnList.GetCounts().enabled())) { if (fLogIPs) { LogPrint(BCLog::COINJOIN, "DSACCEPT -- last dsq too recent, must wait: peer=%d, addr=%s\n", peer.GetId(), peer.addr.ToStringAddrPort()); @@ -193,7 +193,7 @@ void CCoinJoinServer::ProcessDSQUEUE(NodeId from, CDataStream& vRecv) if (!dsq.fReady) { //don't allow a few nodes to dominate the queuing process - if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetValidMNsCount())) { + if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetCounts().enabled())) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- node sending too many dsq messages, masternode=%s\n", dmn->proTxHash.ToString()); return; } diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 511d08549587..5c1f19a508d0 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -21,9 +21,9 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmn const CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : isman{isman}, + credit_pool_manager{std::make_unique(evodb, chainman)}, m_chainlocks{chainlocks}, ehf_manager{std::make_unique(evodb, chainman, qman)}, - credit_pool_manager{std::make_unique(evodb, chainman)}, mn_payments{std::make_unique(dmnman, govman, chainman, consensus_params, mn_sync, sporkman)}, special_tx{std::make_unique(*credit_pool_manager, dmnman, *ehf_manager, qblockman, qsnapman, chainman, consensus_params, chainlocks, qman)} @@ -44,6 +44,12 @@ bool CChainstateHelper::HasChainLock(int nHeight, const uint256& blockHash) cons int32_t CChainstateHelper::GetBestChainLockHeight() const { return m_chainlocks.GetBestChainLockHeight(); } +/** Passthrough functions to CCreditPoolManager */ +CCreditPool CChainstateHelper::GetCreditPool(const CBlockIndex* const pindex) +{ + return credit_pool_manager->GetCreditPool(pindex); +} + /** Passthrough functions to CInstantSendManager */ std::optional> CChainstateHelper::ConflictingISLockIfAny( const CTransaction& tx) const diff --git a/src/evo/chainhelper.h b/src/evo/chainhelper.h index 5b5478d077a7..69a229d93cad 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -13,34 +13,45 @@ class CBlockIndex; class CCreditPoolManager; class CDeterministicMNManager; class CEvoDB; +class CGovernanceManager; class ChainstateManager; +class CMasternodeSync; class CMNHFManager; class CMNPaymentsProcessor; -class CMasternodeSync; -class CGovernanceManager; class CSpecialTxProcessor; class CSporkManager; class CTransaction; class uint256; - +struct CCreditPool; namespace chainlock { class Chainlocks; -} -namespace Consensus { struct Params; } +} // namespace chainlock +namespace Consensus { +struct Params; +} // namespace Consensus namespace llmq { class CInstantSendManager; class CQuorumBlockProcessor; class CQuorumManager; class CQuorumSnapshotManager; -} +} // namespace llmq +namespace node { +class BlockAssembler; +} // namespace node class CChainstateHelper { + friend class node::BlockAssembler; + private: llmq::CInstantSendManager& isman; + const std::unique_ptr credit_pool_manager; public: const chainlock::Chainlocks& m_chainlocks; + const std::unique_ptr ehf_manager; + const std::unique_ptr mn_payments; + const std::unique_ptr special_tx; public: CChainstateHelper() = delete; @@ -54,11 +65,14 @@ class CChainstateHelper const llmq::CQuorumManager& qman); ~CChainstateHelper(); - /** Passthrough functions to chainlock::Chainlocks*/ + /** Passthrough functions to chainlock::Chainlocks */ bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const; bool HasChainLock(int nHeight, const uint256& blockHash) const; int32_t GetBestChainLockHeight() const; + /** Passthrough functions to CCreditPoolManager */ + CCreditPool GetCreditPool(const CBlockIndex* const pindex); + /** Passthrough functions to CInstantSendManager */ std::optional> ConflictingISLockIfAny(const CTransaction& tx) const; bool IsInstantSendWaitingForTx(const uint256& hash) const; @@ -66,12 +80,6 @@ class CChainstateHelper bool ShouldInstantSendRejectConflicts() const; std::unordered_map GetSignalsStage(const CBlockIndex* const pindexPrev); - -public: - const std::unique_ptr ehf_manager; - const std::unique_ptr credit_pool_manager; - const std::unique_ptr mn_payments; - const std::unique_ptr special_tx; }; #endif // BITCOIN_EVO_CHAINHELPER_H diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 32b2f8b61296..cb963d53a808 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -225,7 +225,8 @@ std::vector CDeterministicMNList::GetProjectedMNPayees(gsl } const bool isMNRewardReallocation = DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR); - const auto weighted_count = isMNRewardReallocation ? GetValidMNsCount() : GetValidWeightedMNsCount(); + const auto counts = GetCounts(); + const auto weighted_count = isMNRewardReallocation ? counts.enabled() : counts.m_valid_weighted; nCount = std::min(nCount, int(weighted_count)); std::vector result; @@ -293,7 +294,7 @@ int CDeterministicMNList::CalcMaxPoSePenalty() const // Maximum PoSe penalty is dynamic and equals the number of registered MNs // It's however at least 100. // This means that the max penalty is usually equal to a full payment cycle - return std::max(100, (int)GetAllMNsCount()); + return std::max(100, (int)GetCounts().total()); } int CDeterministicMNList::CalcPenalty(int percent) const @@ -333,7 +334,7 @@ void CDeterministicMNList::PoSePunish(const uint256& proTxHash, int penalty, boo void CDeterministicMNList::DecreaseScores() { std::vector toDecrease; - toDecrease.reserve(GetAllMNsCount() / 10); + toDecrease.reserve(GetCounts().total() / 10); // only iterate and decrease for valid ones (not PoSe banned yet) // if a MN ever reaches the maximum, it stays in PoSe banned state until revived ForEachMNShared(/*onlyValid=*/true, [&toDecrease](const auto& dmn) { @@ -690,7 +691,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_nullnHeight; @@ -706,14 +707,15 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_nullactive()) { - ::g_stats_client->gauge("masternodes.count", newList.GetAllMNsCount()); - ::g_stats_client->gauge("masternodes.weighted_count", newList.GetValidWeightedMNsCount()); - ::g_stats_client->gauge("masternodes.enabled", newList.GetValidMNsCount()); - ::g_stats_client->gauge("masternodes.weighted_enabled", newList.GetValidWeightedMNsCount()); - ::g_stats_client->gauge("masternodes.evo.count", newList.GetAllEvoCount()); - ::g_stats_client->gauge("masternodes.evo.enabled", newList.GetValidEvoCount()); - ::g_stats_client->gauge("masternodes.mn.count", newList.GetAllMNsCount() - newList.GetAllEvoCount()); - ::g_stats_client->gauge("masternodes.mn.enabled", newList.GetValidMNsCount() - newList.GetValidEvoCount()); + const auto counts{newList.GetCounts()}; + ::g_stats_client->gauge("masternodes.count", counts.total()); + ::g_stats_client->gauge("masternodes.weighted_count", counts.m_total_weighted); + ::g_stats_client->gauge("masternodes.enabled", counts.enabled()); + ::g_stats_client->gauge("masternodes.weighted_enabled", counts.m_valid_weighted); + ::g_stats_client->gauge("masternodes.evo.count", counts.m_total_evo); + ::g_stats_client->gauge("masternodes.evo.enabled", counts.m_valid_evo); + ::g_stats_client->gauge("masternodes.mn.count", counts.m_total_mn); + ::g_stats_client->gauge("masternodes.mn.enabled", counts.m_valid_mn); } if (nHeight == consensusParams.DIP0003EnforcementHeight) { diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 51a2bf987bfc..3a684ffa78df 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -142,6 +142,18 @@ class CDeterministicMNList using MnInternalIdMap = immer::map; using MnUniquePropertyMap = immer::map, ImmerHasher>; + struct Counts { + size_t m_total_evo{0}; + size_t m_total_mn{0}; + size_t m_total_weighted{0}; + size_t m_valid_evo{0}; + size_t m_valid_mn{0}; + size_t m_valid_weighted{0}; + + [[nodiscard]] size_t total() const { return m_total_mn + m_total_evo; } + [[nodiscard]] size_t enabled() const { return m_valid_mn + m_valid_evo; } + }; + private: uint256 blockHash; int nHeight{-1}; @@ -253,34 +265,30 @@ class CDeterministicMNList InvalidateSMLCache(); } - [[nodiscard]] size_t GetAllMNsCount() const - { - return mnMap.size(); - } - - [[nodiscard]] size_t GetValidMNsCount() const + [[nodiscard]] Counts GetCounts() const { - return ranges::count_if(mnMap, [](const auto& p) { return !p.second->pdmnState->IsBanned(); }); - } - - [[nodiscard]] size_t GetAllEvoCount() const - { - return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == MnType::Evo; }); - } - - [[nodiscard]] size_t GetValidEvoCount() const - { - return ranges::count_if(mnMap, [](const auto& p) { - return p.second->nType == MnType::Evo && !p.second->pdmnState->IsBanned(); - }); - } - - [[nodiscard]] size_t GetValidWeightedMNsCount() const - { - return std::accumulate(mnMap.begin(), mnMap.end(), 0, [](auto res, const auto& p) { - if (p.second->pdmnState->IsBanned()) return res; - return res + GetMnType(p.second->nType).voting_weight; - }); + Counts ret; + for (const auto& [_, dmn] : mnMap) { + const bool is_evo = dmn->nType == MnType::Evo; + const bool is_valid = !dmn->pdmnState->IsBanned(); + const auto weight = GetMnType(dmn->nType).voting_weight; + if (is_evo) { + ret.m_total_evo++; + if (is_valid) { + ret.m_valid_evo++; + } + } else { + ret.m_total_mn++; + if (is_valid) { + ret.m_valid_mn++; + } + } + if (is_valid) { + ret.m_valid_weighted += weight; + } + ret.m_total_weighted += weight; + } + return ret; } /** @@ -364,7 +372,7 @@ class CDeterministicMNList /** * Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct * as PoSe banning might occur later - * @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetValidWeightedMNsCount twice. + * @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetCounts twice. */ [[nodiscard]] std::vector GetProjectedMNPayees(gsl::not_null pindexPrev, int nCount = std::numeric_limits::max()) const; diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 4a7af81ee2b6..c1434cc9acc7 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -273,7 +273,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul LogPrintf("%s -- MN %s removed from list because collateral was used for " /* Continued */ "a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n", __func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), - nHeight, newList.GetAllMNsCount()); + nHeight, newList.GetCounts().total()); } } @@ -467,7 +467,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul LogPrintf("%s -- MN %s removed from list because collateral was spent. " /* Continued */ "collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n", __func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, - newList.GetAllMNsCount()); + newList.GetCounts().total()); } } } diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 6eaa1ed71c89..9fb476a7333a 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1466,7 +1466,7 @@ std::vector> CGovernanceManager::GetApp // A proposal is considered passing if (YES votes) >= (Total Weight of Masternodes / 10), // count total valid (ENABLED) masternodes to determine passing threshold. - const int nWeightedMnCount = tip_mn_list.GetValidWeightedMNsCount(); + const int nWeightedMnCount = tip_mn_list.GetCounts().m_valid_weighted; const int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nWeightedMnCount / 10); LOCK(cs_store); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 956f209b4292..5d78d10dd348 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -610,7 +610,7 @@ void CGovernanceObject::UpdateSentinelVariables(const CDeterministicMNList& tip_ // CALCULATE MINIMUM SUPPORT LEVELS REQUIRED - int nWeightedMnCount = (int)tip_mn_list.GetValidWeightedMNsCount(); + int nWeightedMnCount = (int)tip_mn_list.GetCounts().m_valid_weighted; if (nWeightedMnCount == 0) return; // CALCULATE THE MINIMUM VOTE COUNT REQUIRED FOR FULL SIGNAL diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 449ceeaf5458..0726da6911e3 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -721,6 +721,22 @@ size_t CInstantSendManager::GetInstantSendLockCount() const return db.GetInstantSendLockCount(); } +CInstantSendManager::Counts CInstantSendManager::GetCounts() const +{ + Counts ret; + ret.m_verified = db.GetInstantSendLockCount(); + { + LOCK(cs_pendingLocks); + ret.m_unverified = pendingInstantSendLocks.size(); + ret.m_awaiting_tx = pendingNoTxInstantSendLocks.size(); + } + { + LOCK(cs_nonLocked); + ret.m_unprotected_tx = nonLockedTxs.size(); + } + return ret; +} + void CInstantSendManager::CacheBlockHeightInternal(const CBlockIndex* const block_index) const { AssertLockHeld(cs_height_cache); diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 8c3ec763f05d..dde3739d6030 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -186,6 +186,14 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent size_t GetInstantSendLockCount() const; + struct Counts { + size_t m_verified{0}; + size_t m_unverified{0}; + size_t m_awaiting_tx{0}; + size_t m_unprotected_tx{0}; + }; + Counts GetCounts() const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks, !cs_nonLocked); + void CacheBlockHeight(const CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); std::optional GetBlockHeight(const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); void CacheTipHeight(const CBlockIndex* const tip) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 13cab2f924ca..c25e83112001 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -88,7 +88,7 @@ class MnEntry virtual const uint256& getProTxHash() const = 0; }; -using MnEntryCPtr = std::unique_ptr; +using MnEntryCPtr = std::shared_ptr; //! Interface for a list of masternode entries class MnList @@ -99,15 +99,22 @@ class MnList MnList() = delete; + struct Counts { + size_t m_total_evo{0}; + size_t m_total_mn{0}; + size_t m_total_weighted{0}; + size_t m_valid_evo{0}; + size_t m_valid_mn{0}; + size_t m_valid_weighted{0}; + + [[nodiscard]] size_t total() const { return m_total_mn + m_total_evo; } + [[nodiscard]] size_t enabled() const { return m_valid_mn + m_valid_evo; } + }; + virtual Counts getCounts() const = 0; virtual int32_t getHeight() const = 0; - virtual size_t getAllEvoCount() const = 0; - virtual size_t getAllMNsCount() const = 0; - virtual size_t getValidEvoCount() const = 0; - virtual size_t getValidMNsCount() const = 0; - virtual size_t getValidWeightedMNsCount() const = 0; virtual uint256 getBlockHash() const = 0; - virtual void forEachMN(bool only_valid, std::function cb) const = 0; + virtual void forEachMN(bool only_valid, std::function cb) const = 0; virtual MnEntryCPtr getMN(const uint256& hash) const = 0; virtual MnEntryCPtr getMNByService(const CService& service) const = 0; virtual MnEntryCPtr getValidMN(const uint256& hash) const = 0; @@ -177,7 +184,36 @@ class LLMQ { public: virtual ~LLMQ() {} - virtual size_t getInstantSentLockCount() = 0; + struct ChainLockInfo { + int32_t m_height{0}; + int64_t m_block_time{0}; + uint256 m_hash{}; + }; + virtual ChainLockInfo getBestChainLock() = 0; + struct CreditPoolCounts { + CAmount m_diff{0}; + CAmount m_limit{0}; + CAmount m_locked{0}; + }; + virtual CreditPoolCounts getCreditPoolCounts() = 0; + struct InstantSendCounts { + size_t m_verified{0}; + size_t m_unverified{0}; + size_t m_awaiting_tx{0}; + size_t m_unprotected_tx{0}; + }; + virtual InstantSendCounts getInstantSendCounts() = 0; + virtual size_t getPendingAssetUnlocks() = 0; + struct QuorumInfo { + std::string m_name; + size_t m_count{0}; + double m_health{0.0}; + bool m_rotates{false}; + int32_t m_data_retention_blocks{0}; + int32_t m_newest_height{0}; + int32_t m_expiry_height{0}; + }; + virtual std::vector getQuorumStats() = 0; virtual void setContext(node::NodeContext* context) {} }; diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 71720b6a3ba1..8106d893fbeb 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -134,7 +134,7 @@ std::vector CalculateScoresForQuorum(const CDeterministicMNList const bool onlyEvoNodes) { std::vector scores; - scores.reserve(mn_list.GetAllMNsCount()); + scores.reserve(mn_list.GetCounts().total()); mn_list.ForEachMNShared(/*onlyValid=*/true, [&](const auto& dmn) { if (dmn->pdmnState->confirmedHash.IsNull()) { @@ -310,12 +310,13 @@ void BuildQuorumSnapshot(const Consensus::LLMQParams& llmqParams, const Consensu return; } - quorumSnapshot.activeQuorumMembers.resize(allMns.GetAllMNsCount()); + const auto allMnsTotal = allMns.GetCounts().total(); + quorumSnapshot.activeQuorumMembers.resize(allMnsTotal); const auto modifier = GetHashModifier(llmqParams, consensus_params, pCycleQuorumBaseBlockIndex); auto sortedAllMns = CalculateQuorum(allMns, modifier); LogPrint(BCLog::LLMQ, "BuildQuorumSnapshot h[%d] numMns[%d]\n", pCycleQuorumBaseBlockIndex->nHeight, - allMns.GetAllMNsCount()); + allMnsTotal); std::fill(quorumSnapshot.activeQuorumMembers.begin(), quorumSnapshot.activeQuorumMembers.end(), false); size_t index = {}; @@ -352,7 +353,7 @@ std::vector BuildNewQuorumQuarterMembers(const Consensus::LLMQPar auto quarterSize{quorumSize / 4}; const auto modifier = GetHashModifier(llmqParams, util_params.m_chainman.GetConsensus(), util_params.m_base_index); - if (allMns.GetValidMNsCount() < quarterSize) { + if (allMns.GetCounts().enabled() < quarterSize) { return quarterQuorumMembers; } @@ -408,7 +409,7 @@ std::vector BuildNewQuorumQuarterMembers(const Consensus::LLMQPar size_t firstSkippedIndex = 0; size_t idx{0}; for (const size_t i : irange::range(nQuorums)) { - auto usedMNsCount = MnsUsedAtHIndexed[i].GetAllMNsCount(); + auto usedMNsCount = MnsUsedAtHIndexed[i].GetCounts().total(); bool updated{false}; size_t initial_loop_idx = idx; while (quarterQuorumMembers[i].size() < quarterSize && (usedMNsCount + quarterQuorumMembers[i].size() < sortedCombinedMnsList.size())) { @@ -468,7 +469,7 @@ std::vector ComputeQuorumMembersByQuarterRotation(const Consensus llmq::WORK_DIFF_DEPTH); CDeterministicMNList allMns = util_params.m_dmnman.GetListForBlock(pWorkBlockIndex); LogPrint(BCLog::LLMQ, "ComputeQuorumMembersByQuarterRotation llmqType[%d] nHeight[%d] allMns[%d]\n", - ToUnderlying(llmqParams.type), util_params.m_base_index->nHeight, allMns.GetValidMNsCount()); + ToUnderlying(llmqParams.type), util_params.m_base_index->nHeight, allMns.GetCounts().enabled()); PreviousQuorumQuarters previousQuarters(nQuorums); auto prev_cycles{previousQuarters.GetCycles()}; diff --git a/src/masternode/payments.cpp b/src/masternode/payments.cpp index 06c5691b064f..29b1edb44a39 100644 --- a/src/masternode/payments.cpp +++ b/src/masternode/payments.cpp @@ -53,7 +53,7 @@ CAmount PlatformShare(const CAmount reward) voutMasternodePaymentsRet.emplace_back(platformReward, CScript() << OP_RETURN); } const auto mnList = m_dmnman.GetListForBlock(pindexPrev); - if (mnList.GetAllMNsCount() == 0) { + if (mnList.GetCounts().total() == 0) { LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- no masternode registered to receive a payment\n", __func__); return true; } diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 00a40471cb2b..d6aa31fddf26 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -26,7 +28,11 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include @@ -137,40 +143,47 @@ class MnListImpl : public MnList } ~MnListImpl() = default; + Counts getCounts() const override + { + const auto counts{m_list.GetCounts()}; + return { + .m_total_evo = counts.m_total_evo, + .m_total_mn = counts.m_total_mn, + .m_total_weighted = counts.m_total_weighted, + .m_valid_evo = counts.m_valid_evo, + .m_valid_mn = counts.m_valid_mn, + .m_valid_weighted = counts.m_valid_weighted, + }; + } int32_t getHeight() const override { return m_list.GetHeight(); } - size_t getAllEvoCount() const override { return m_list.GetAllEvoCount(); } - size_t getAllMNsCount() const override { return m_list.GetAllMNsCount(); } - size_t getValidEvoCount() const override { return m_list.GetValidEvoCount(); } - size_t getValidMNsCount() const override { return m_list.GetValidMNsCount(); } - size_t getValidWeightedMNsCount() const override { return m_list.GetValidWeightedMNsCount(); } uint256 getBlockHash() const override { return m_list.GetBlockHash(); } - void forEachMN(bool only_valid, std::function cb) const override + void forEachMN(bool only_valid, std::function cb) const override { m_list.ForEachMNShared(only_valid, [&cb](const auto& dmn) { - cb(MnEntryImpl{dmn}); + cb(std::make_shared(dmn)); }); } MnEntryCPtr getMN(const uint256& hash) const override { const auto dmn{m_list.GetMN(hash)}; - return dmn ? std::make_unique(dmn) : nullptr; + return dmn ? std::make_shared(dmn) : nullptr; } MnEntryCPtr getMNByService(const CService& service) const override { const auto dmn{m_list.GetMNByService(service)}; - return dmn ? std::make_unique(dmn) : nullptr; + return dmn ? std::make_shared(dmn) : nullptr; } MnEntryCPtr getValidMN(const uint256& hash) const override { const auto dmn{m_list.GetValidMN(hash)}; - return dmn ? std::make_unique(dmn) : nullptr; + return dmn ? std::make_shared(dmn) : nullptr; } std::vector getProjectedMNPayees(const CBlockIndex* pindex) const override { std::vector ret; for (const auto& payee : m_list.GetProjectedMNPayees(pindex)) { - ret.emplace_back(std::make_unique(payee)); + ret.emplace_back(std::make_shared(payee)); } return ret; } @@ -202,13 +215,15 @@ class EVOImpl : public EVO public: std::pair getListAtChainTip() override { - const CBlockIndex *tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip()); - MnListImpl mnList{CDeterministicMNList{}}; - if (tip != nullptr && context().dmnman != nullptr) { - mnList = context().dmnman->GetListForBlock(tip); + const auto *tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip()); + if (tip && context().dmnman) { + MnListImpl mnList = context().dmnman->GetListForBlock(tip); + if (!mnList.getBlockHash().IsNull()) { + mnList.setContext(m_context); + return {std::make_shared(mnList), tip}; + } } - mnList.setContext(m_context); - return {std::make_shared(mnList), tip}; + return {nullptr, nullptr}; } void setContext(NodeContext* context) override { @@ -293,7 +308,7 @@ class GOVImpl : public GOV CSuperblock::GetNearestSuperblocksHeights(context().chainman->ActiveHeight(), info.lastsuperblock, info.nextsuperblock); info.governancebudget = CSuperblock::GetPaymentsLimit(context().chainman->ActiveChain(), info.nextsuperblock); if (context().dmnman) { - info.fundingthreshold = context().dmnman->GetListAtChainTip().GetValidWeightedMNsCount() / 10; + info.fundingthreshold = static_cast(context().dmnman->GetListAtChainTip().GetCounts().m_valid_weighted / 10); } } info.proposalfee = GOVERNANCE_PROPOSAL_FEE_TX; @@ -420,12 +435,100 @@ class LLMQImpl : public LLMQ NodeContext& context() { return *Assert(m_context); } public: - size_t getInstantSentLockCount() override + CreditPoolCounts getCreditPoolCounts() override { - if (context().llmq_ctx && context().llmq_ctx->isman != nullptr) { - return context().llmq_ctx->isman->GetInstantSendLockCount(); + CreditPoolCounts ret{}; + if (!context().chainman) { + return ret; + } + const auto* pindex{WITH_LOCK(::cs_main, return context().chainman->ActiveChain().Tip())}; + if (!pindex || !pindex->pprev) { + return ret; + } + auto& chain_helper{context().chainman->ActiveChainstate().ChainHelper()}; + const auto pool{chain_helper.GetCreditPool(pindex)}; + ret.m_locked = pool.locked; + ret.m_limit = pool.currentLimit; + ret.m_diff = pool.locked - chain_helper.GetCreditPool(pindex->pprev).locked; + return ret; + } + ChainLockInfo getBestChainLock() override + { + if (!context().chainlocks) { + return {}; + } + const auto [clsig, pindex] = context().chainlocks->GetBestChainlockWithPindex(); + if (!pindex) { + return {}; + } + return { + .m_height = clsig.getHeight(), + .m_block_time = pindex->GetBlockTime(), + .m_hash = clsig.getBlockHash(), + }; + } + InstantSendCounts getInstantSendCounts() override + { + if (!context().llmq_ctx || !context().llmq_ctx->isman) { + return {}; + } + const auto counts{context().llmq_ctx->isman->GetCounts()}; + return { + .m_verified = counts.m_verified, + .m_unverified = counts.m_unverified, + .m_awaiting_tx = counts.m_awaiting_tx, + .m_unprotected_tx = counts.m_unprotected_tx, + }; + } + size_t getPendingAssetUnlocks() override + { + if (!context().mempool) { + return 0; + } + LOCK(context().mempool->cs); + return static_cast(ranges::count_if(context().mempool->mapTx, [](const auto& entry) { + return entry.GetTx().IsPlatformTransfer(); + })); + } + std::vector getQuorumStats() override + { + std::vector stats{}; + if (!context().llmq_ctx || !context().llmq_ctx->qman || !context().chainman) { + return stats; + } + const auto* pindex{WITH_LOCK(::cs_main, return context().chainman->ActiveChain().Tip())}; + if (!pindex) { + return stats; + } + for (const auto& type : llmq::GetEnabledQuorumTypes(*context().chainman, pindex)) { + const auto llmq_params{Params().GetLLMQ(type)}; + if (!llmq_params.has_value()) { + continue; + } + const auto quorums{context().llmq_ctx->qman->ScanQuorums(type, pindex, llmq_params->signingActiveQuorumCount)}; + double health{0.0}; + for (const auto& q : quorums) { + size_t numMembers = q->members.size(); + size_t numValidMembers = q->qc->CountValidMembers(); + health += (numMembers > 0) ? (double(numValidMembers) / double(numMembers)) : 0.0; + } + health = quorums.empty() ? 0.0 : (health / quorums.size()); + const int32_t newest_height{(!quorums.empty() && quorums[0]->m_quorum_base_block_index) + ? quorums[0]->m_quorum_base_block_index->nHeight : 0}; + const int32_t expiry_height{(newest_height > 0) + ? newest_height + llmq_params->signingActiveQuorumCount * llmq_params->dkgInterval + : 0}; + stats.emplace_back(QuorumInfo{ + .m_name = std::string(llmq_params->name), + .m_count = quorums.size(), + .m_health = health, + .m_rotates = llmq_params->useRotation, + .m_data_retention_blocks = llmq_params->max_store_depth(), + .m_newest_height = newest_height, + .m_expiry_height = expiry_height, + }); } - return 0; + return stats; } void setContext(NodeContext* context) override { diff --git a/src/node/miner.cpp b/src/node/miner.cpp index e5ee48a78293..919db864a008 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -468,7 +468,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // duplicates of indexes. There's used `BlockSubsidy` equaled to 0 std::optional creditPoolDiff; if (DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_V20)) { - CCreditPool creditPool = m_chain_helper.credit_pool_manager->GetCreditPool(pindexPrev); + CCreditPool creditPool = m_chain_helper.GetCreditPool(pindexPrev); creditPoolDiff.emplace(std::move(creditPool), pindexPrev, chainparams.GetConsensus(), 0); } diff --git a/src/node/sync_manager.cpp b/src/node/sync_manager.cpp index 8acf829afd67..6d6d53259e36 100644 --- a/src/node/sync_manager.cpp +++ b/src/node/sync_manager.cpp @@ -76,7 +76,7 @@ int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesC if (Params().NetworkIDString() != CBaseChainParams::MAIN) { nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / - std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); + std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetCounts().enabled()))); } static Mutex cs_recently; diff --git a/src/qt/clientfeeds.cpp b/src/qt/clientfeeds.cpp index a50fedaaf654..e25f907cbd0d 100644 --- a/src/qt/clientfeeds.cpp +++ b/src/qt/clientfeeds.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include