Skip to content

Commit dcfa6ba

Browse files
Merge dashpay#7160: feat(interfaces): consolidate masternode counts into one struct, expose chainlock, instantsend, credit pool, quorum statistics
ce15f98 build: update semantic pull requests to include interfaces as scope (Kittywhiskers Van Gogh) fe8b372 interfaces: expose quorum statistics (health, rotation, expiry, newest) (Kittywhiskers Van Gogh) 9c37174 interfaces: expose credit pool pending asset unlocks (Kittywhiskers Van Gogh) 399eb83 refactor: make `GetCreditPool()` a chainhelper passthrough, add interf. (Kittywhiskers Van Gogh) 9f8168c interfaces: expose instantsend counters (verified, awaiting, unprotected) (Kittywhiskers Van Gogh) f534097 interfaces: expose best chainlock's height, block time and hash (Kittywhiskers Van Gogh) 50b3f89 fix: return null `getListAtChainTip()` retval instead of partial retval (Kittywhiskers Van Gogh) 93b6e2b fix: correctly track total weighted instead of conflating with valid (Kittywhiskers Van Gogh) 7b65b76 refactor: consolidate masternode counts into one struct (Kittywhiskers Van Gogh) baafee5 refactor: use `shared_ptr` for `MnEntryCPtr` (Kittywhiskers Van Gogh) 892ad7c refactor(qt): cache proposal JSON, drop unused `isActive()`, drop copy (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Dependency for dashpay#7118 Spun off from [dash#7118](dashpay#7118) for ease of review, exposes new interface methods for usage in feeds as part of UI refresh. ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ **(note: N/A)** ACKs for top commit: PastaPastaPasta: utACK ce15f98 Tree-SHA512: ca9fc74b7499e77c306a3955cd9887a752c63e412833db605caee2f8ea72790733f931c01bda0b74cfcf2cb82cb07376f9cdaf43d39bf0476a222953da756a41
2 parents 38bedd8 + ce15f98 commit dcfa6ba

32 files changed

+364
-196
lines changed

.github/workflows/semantic-pull-request.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ jobs:
3838
# Configure which scopes are allowed (newline delimited).
3939
scopes: |
4040
consensus
41+
interfaces
4142
log
4243
mining
4344
net
4445
qt
4546
rest
4647
rpc
4748
scripts
49+
stats
4850
utils
4951
wallet
5052
zmq
51-
stats

src/active/quorums.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ size_t QuorumParticipant::GetQuorumRecoveryStartOffset(const CQuorum& quorum, gs
8989
{
9090
auto mns = m_dmnman.GetListForBlock(pIndex);
9191
std::vector<uint256> vecProTxHashes;
92-
vecProTxHashes.reserve(mns.GetValidMNsCount());
92+
vecProTxHashes.reserve(mns.GetCounts().enabled());
9393
mns.ForEachMN(/*onlyValid=*/true,
9494
[&](const auto& pMasternode) { vecProTxHashes.emplace_back(pMasternode.proTxHash); });
9595
std::sort(vecProTxHashes.begin(), vecProTxHashes.end());

src/coinjoin/client.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ MessageProcessingResult CCoinJoinClientQueueManager::ProcessMessage(NodeId from,
124124
LogPrint(BCLog::COINJOIN, "DSQUEUE -- CoinJoin queue is ready, masternode=%s, queue=%s\n", dmn->proTxHash.ToString(), dsq.ToString());
125125
return ret;
126126
} else {
127-
if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetValidMNsCount())) {
127+
if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetCounts().enabled())) {
128128
LogPrint(BCLog::COINJOIN, "DSQUEUE -- Masternode %s is sending too many dsq messages\n",
129129
dmn->proTxHash.ToString());
130130
return ret;
@@ -826,7 +826,7 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(ChainstateManager& chainman
826826
return false;
827827
}
828828

829-
if (m_dmnman.GetListAtChainTip().GetValidMNsCount() == 0 &&
829+
if (m_dmnman.GetListAtChainTip().GetCounts().enabled() == 0 &&
830830
Params().NetworkIDString() != CBaseChainParams::REGTEST) {
831831
strAutoDenomResult = _("No Masternodes detected.");
832832
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::DoAutomaticDenominating -- %s\n", strAutoDenomResult.original);
@@ -988,7 +988,7 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(ChainstateManager& chainman
988988
return false;
989989
}
990990

991-
int nMnCountEnabled = m_dmnman.GetListAtChainTip().GetValidMNsCount();
991+
int nMnCountEnabled = m_dmnman.GetListAtChainTip().GetCounts().enabled();
992992

993993
// If we've used 90% of the Masternode list then drop the oldest first ~30%
994994
int nThreshold_high = nMnCountEnabled * 0.9;
@@ -1033,7 +1033,7 @@ CDeterministicMNCPtr CCoinJoinClientManager::GetRandomNotUsedMasternode()
10331033
{
10341034
auto mnList = m_dmnman.GetListAtChainTip();
10351035

1036-
size_t nCountEnabled = mnList.GetValidMNsCount();
1036+
size_t nCountEnabled = mnList.GetCounts().enabled();
10371037
size_t nCountNotExcluded{nCountEnabled - m_mn_metaman.GetUsedMasternodesCount()};
10381038

10391039
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,
10781078
if (m_queueman == nullptr) return false;
10791079

10801080
const auto mnList = m_dmnman.GetListAtChainTip();
1081-
const int nWeightedMnCount = mnList.GetValidWeightedMNsCount();
1081+
const int nWeightedMnCount = mnList.GetCounts().m_valid_weighted;
10821082

10831083
// Look through the queues and see if anything matches
10841084
CCoinJoinQueue dsq;
@@ -1143,8 +1143,9 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon
11431143

11441144
int nTries = 0;
11451145
const auto mnList = m_dmnman.GetListAtChainTip();
1146-
const int nMnCount = mnList.GetValidMNsCount();
1147-
const int nWeightedMnCount = mnList.GetValidWeightedMNsCount();
1146+
const auto mnCounts = mnList.GetCounts();
1147+
const int nMnCount = mnCounts.enabled();
1148+
const int nWeightedMnCount = mnCounts.m_valid_weighted;
11481149

11491150
// find available denominated amounts
11501151
std::set<CAmount> setAmounts;

src/coinjoin/server.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ void CCoinJoinServer::ProcessDSACCEPT(CNode& peer, CDataStream& vRecv)
100100
}
101101
}
102102

103-
if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, mnList.GetValidMNsCount())) {
103+
if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, mnList.GetCounts().enabled())) {
104104
if (fLogIPs) {
105105
LogPrint(BCLog::COINJOIN, "DSACCEPT -- last dsq too recent, must wait: peer=%d, addr=%s\n",
106106
peer.GetId(), peer.addr.ToStringAddrPort());
@@ -193,7 +193,7 @@ void CCoinJoinServer::ProcessDSQUEUE(NodeId from, CDataStream& vRecv)
193193

194194
if (!dsq.fReady) {
195195
//don't allow a few nodes to dominate the queuing process
196-
if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetValidMNsCount())) {
196+
if (m_mn_metaman.IsMixingThresholdExceeded(dmn->proTxHash, tip_mn_list.GetCounts().enabled())) {
197197
LogPrint(BCLog::COINJOIN, "DSQUEUE -- node sending too many dsq messages, masternode=%s\n", dmn->proTxHash.ToString());
198198
return;
199199
}

src/evo/chainhelper.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmn
2121
const CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks,
2222
const llmq::CQuorumManager& qman) :
2323
isman{isman},
24+
credit_pool_manager{std::make_unique<CCreditPoolManager>(evodb, chainman)},
2425
m_chainlocks{chainlocks},
2526
ehf_manager{std::make_unique<CMNHFManager>(evodb, chainman, qman)},
26-
credit_pool_manager{std::make_unique<CCreditPoolManager>(evodb, chainman)},
2727
mn_payments{std::make_unique<CMNPaymentsProcessor>(dmnman, govman, chainman, consensus_params, mn_sync, sporkman)},
2828
special_tx{std::make_unique<CSpecialTxProcessor>(*credit_pool_manager, dmnman, *ehf_manager, qblockman, qsnapman,
2929
chainman, consensus_params, chainlocks, qman)}
@@ -44,6 +44,12 @@ bool CChainstateHelper::HasChainLock(int nHeight, const uint256& blockHash) cons
4444

4545
int32_t CChainstateHelper::GetBestChainLockHeight() const { return m_chainlocks.GetBestChainLockHeight(); }
4646

47+
/** Passthrough functions to CCreditPoolManager */
48+
CCreditPool CChainstateHelper::GetCreditPool(const CBlockIndex* const pindex)
49+
{
50+
return credit_pool_manager->GetCreditPool(pindex);
51+
}
52+
4753
/** Passthrough functions to CInstantSendManager */
4854
std::optional<std::pair</*islock_hash=*/uint256, /*txid=*/uint256>> CChainstateHelper::ConflictingISLockIfAny(
4955
const CTransaction& tx) const

src/evo/chainhelper.h

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,45 @@ class CBlockIndex;
1313
class CCreditPoolManager;
1414
class CDeterministicMNManager;
1515
class CEvoDB;
16+
class CGovernanceManager;
1617
class ChainstateManager;
18+
class CMasternodeSync;
1719
class CMNHFManager;
1820
class CMNPaymentsProcessor;
19-
class CMasternodeSync;
20-
class CGovernanceManager;
2121
class CSpecialTxProcessor;
2222
class CSporkManager;
2323
class CTransaction;
2424
class uint256;
25-
25+
struct CCreditPool;
2626
namespace chainlock {
2727
class Chainlocks;
28-
}
29-
namespace Consensus { struct Params; }
28+
} // namespace chainlock
29+
namespace Consensus {
30+
struct Params;
31+
} // namespace Consensus
3032
namespace llmq {
3133
class CInstantSendManager;
3234
class CQuorumBlockProcessor;
3335
class CQuorumManager;
3436
class CQuorumSnapshotManager;
35-
}
37+
} // namespace llmq
38+
namespace node {
39+
class BlockAssembler;
40+
} // namespace node
3641

3742
class CChainstateHelper
3843
{
44+
friend class node::BlockAssembler;
45+
3946
private:
4047
llmq::CInstantSendManager& isman;
48+
const std::unique_ptr<CCreditPoolManager> credit_pool_manager;
4149

4250
public:
4351
const chainlock::Chainlocks& m_chainlocks;
52+
const std::unique_ptr<CMNHFManager> ehf_manager;
53+
const std::unique_ptr<CMNPaymentsProcessor> mn_payments;
54+
const std::unique_ptr<CSpecialTxProcessor> special_tx;
4455

4556
public:
4657
CChainstateHelper() = delete;
@@ -54,24 +65,21 @@ class CChainstateHelper
5465
const llmq::CQuorumManager& qman);
5566
~CChainstateHelper();
5667

57-
/** Passthrough functions to chainlock::Chainlocks*/
68+
/** Passthrough functions to chainlock::Chainlocks */
5869
bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const;
5970
bool HasChainLock(int nHeight, const uint256& blockHash) const;
6071
int32_t GetBestChainLockHeight() const;
6172

73+
/** Passthrough functions to CCreditPoolManager */
74+
CCreditPool GetCreditPool(const CBlockIndex* const pindex);
75+
6276
/** Passthrough functions to CInstantSendManager */
6377
std::optional<std::pair</*islock_hash=*/uint256, /*txid=*/uint256>> ConflictingISLockIfAny(const CTransaction& tx) const;
6478
bool IsInstantSendWaitingForTx(const uint256& hash) const;
6579
bool RemoveConflictingISLockByTx(const CTransaction& tx);
6680
bool ShouldInstantSendRejectConflicts() const;
6781

6882
std::unordered_map<uint8_t, int> GetSignalsStage(const CBlockIndex* const pindexPrev);
69-
70-
public:
71-
const std::unique_ptr<CMNHFManager> ehf_manager;
72-
const std::unique_ptr<CCreditPoolManager> credit_pool_manager;
73-
const std::unique_ptr<CMNPaymentsProcessor> mn_payments;
74-
const std::unique_ptr<CSpecialTxProcessor> special_tx;
7583
};
7684

7785
#endif // BITCOIN_EVO_CHAINHELPER_H

src/evo/deterministicmns.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(gsl
225225
}
226226
const bool isMNRewardReallocation = DeploymentActiveAfter(pindexPrev, Params().GetConsensus(),
227227
Consensus::DEPLOYMENT_MN_RR);
228-
const auto weighted_count = isMNRewardReallocation ? GetValidMNsCount() : GetValidWeightedMNsCount();
228+
const auto counts = GetCounts();
229+
const auto weighted_count = isMNRewardReallocation ? counts.enabled() : counts.m_valid_weighted;
229230
nCount = std::min(nCount, int(weighted_count));
230231

231232
std::vector<CDeterministicMNCPtr> result;
@@ -293,7 +294,7 @@ int CDeterministicMNList::CalcMaxPoSePenalty() const
293294
// Maximum PoSe penalty is dynamic and equals the number of registered MNs
294295
// It's however at least 100.
295296
// This means that the max penalty is usually equal to a full payment cycle
296-
return std::max(100, (int)GetAllMNsCount());
297+
return std::max(100, (int)GetCounts().total());
297298
}
298299

299300
int CDeterministicMNList::CalcPenalty(int percent) const
@@ -333,7 +334,7 @@ void CDeterministicMNList::PoSePunish(const uint256& proTxHash, int penalty, boo
333334
void CDeterministicMNList::DecreaseScores()
334335
{
335336
std::vector<CDeterministicMNCPtr> toDecrease;
336-
toDecrease.reserve(GetAllMNsCount() / 10);
337+
toDecrease.reserve(GetCounts().total() / 10);
337338
// only iterate and decrease for valid ones (not PoSe banned yet)
338339
// if a MN ever reaches the maximum, it stays in PoSe banned state until revived
339340
ForEachMNShared(/*onlyValid=*/true, [&toDecrease](const auto& dmn) {
@@ -690,7 +691,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<co
690691
m_evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList);
691692
mnListsCache.emplace(newList.GetBlockHash(), newList);
692693
LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d\n",
693-
__func__, nHeight, newList.GetAllMNsCount());
694+
__func__, nHeight, newList.GetCounts().total());
694695
}
695696

696697
diff.nHeight = pindex->nHeight;
@@ -706,14 +707,15 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<co
706707
}
707708

708709
if (::g_stats_client->active()) {
709-
::g_stats_client->gauge("masternodes.count", newList.GetAllMNsCount());
710-
::g_stats_client->gauge("masternodes.weighted_count", newList.GetValidWeightedMNsCount());
711-
::g_stats_client->gauge("masternodes.enabled", newList.GetValidMNsCount());
712-
::g_stats_client->gauge("masternodes.weighted_enabled", newList.GetValidWeightedMNsCount());
713-
::g_stats_client->gauge("masternodes.evo.count", newList.GetAllEvoCount());
714-
::g_stats_client->gauge("masternodes.evo.enabled", newList.GetValidEvoCount());
715-
::g_stats_client->gauge("masternodes.mn.count", newList.GetAllMNsCount() - newList.GetAllEvoCount());
716-
::g_stats_client->gauge("masternodes.mn.enabled", newList.GetValidMNsCount() - newList.GetValidEvoCount());
710+
const auto counts{newList.GetCounts()};
711+
::g_stats_client->gauge("masternodes.count", counts.total());
712+
::g_stats_client->gauge("masternodes.weighted_count", counts.m_total_weighted);
713+
::g_stats_client->gauge("masternodes.enabled", counts.enabled());
714+
::g_stats_client->gauge("masternodes.weighted_enabled", counts.m_valid_weighted);
715+
::g_stats_client->gauge("masternodes.evo.count", counts.m_total_evo);
716+
::g_stats_client->gauge("masternodes.evo.enabled", counts.m_valid_evo);
717+
::g_stats_client->gauge("masternodes.mn.count", counts.m_total_mn);
718+
::g_stats_client->gauge("masternodes.mn.enabled", counts.m_valid_mn);
717719
}
718720

719721
if (nHeight == consensusParams.DIP0003EnforcementHeight) {

src/evo/deterministicmns.h

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,18 @@ class CDeterministicMNList
142142
using MnInternalIdMap = immer::map<uint64_t, uint256>;
143143
using MnUniquePropertyMap = immer::map<uint256, std::pair<uint256, uint32_t>, ImmerHasher>;
144144

145+
struct Counts {
146+
size_t m_total_evo{0};
147+
size_t m_total_mn{0};
148+
size_t m_total_weighted{0};
149+
size_t m_valid_evo{0};
150+
size_t m_valid_mn{0};
151+
size_t m_valid_weighted{0};
152+
153+
[[nodiscard]] size_t total() const { return m_total_mn + m_total_evo; }
154+
[[nodiscard]] size_t enabled() const { return m_valid_mn + m_valid_evo; }
155+
};
156+
145157
private:
146158
uint256 blockHash;
147159
int nHeight{-1};
@@ -253,34 +265,30 @@ class CDeterministicMNList
253265
InvalidateSMLCache();
254266
}
255267

256-
[[nodiscard]] size_t GetAllMNsCount() const
257-
{
258-
return mnMap.size();
259-
}
260-
261-
[[nodiscard]] size_t GetValidMNsCount() const
268+
[[nodiscard]] Counts GetCounts() const
262269
{
263-
return ranges::count_if(mnMap, [](const auto& p) { return !p.second->pdmnState->IsBanned(); });
264-
}
265-
266-
[[nodiscard]] size_t GetAllEvoCount() const
267-
{
268-
return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == MnType::Evo; });
269-
}
270-
271-
[[nodiscard]] size_t GetValidEvoCount() const
272-
{
273-
return ranges::count_if(mnMap, [](const auto& p) {
274-
return p.second->nType == MnType::Evo && !p.second->pdmnState->IsBanned();
275-
});
276-
}
277-
278-
[[nodiscard]] size_t GetValidWeightedMNsCount() const
279-
{
280-
return std::accumulate(mnMap.begin(), mnMap.end(), 0, [](auto res, const auto& p) {
281-
if (p.second->pdmnState->IsBanned()) return res;
282-
return res + GetMnType(p.second->nType).voting_weight;
283-
});
270+
Counts ret;
271+
for (const auto& [_, dmn] : mnMap) {
272+
const bool is_evo = dmn->nType == MnType::Evo;
273+
const bool is_valid = !dmn->pdmnState->IsBanned();
274+
const auto weight = GetMnType(dmn->nType).voting_weight;
275+
if (is_evo) {
276+
ret.m_total_evo++;
277+
if (is_valid) {
278+
ret.m_valid_evo++;
279+
}
280+
} else {
281+
ret.m_total_mn++;
282+
if (is_valid) {
283+
ret.m_valid_mn++;
284+
}
285+
}
286+
if (is_valid) {
287+
ret.m_valid_weighted += weight;
288+
}
289+
ret.m_total_weighted += weight;
290+
}
291+
return ret;
284292
}
285293

286294
/**
@@ -364,7 +372,7 @@ class CDeterministicMNList
364372
/**
365373
* Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct
366374
* as PoSe banning might occur later
367-
* @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetValidWeightedMNsCount twice.
375+
* @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetCounts twice.
368376
*/
369377
[[nodiscard]] std::vector<CDeterministicMNCPtr> GetProjectedMNPayees(gsl::not_null<const CBlockIndex* const> pindexPrev, int nCount = std::numeric_limits<int>::max()) const;
370378

src/evo/specialtxman.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul
273273
LogPrintf("%s -- MN %s removed from list because collateral was used for " /* Continued */
274274
"a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
275275
__func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(),
276-
nHeight, newList.GetAllMNsCount());
276+
nHeight, newList.GetCounts().total());
277277
}
278278
}
279279

@@ -467,7 +467,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul
467467
LogPrintf("%s -- MN %s removed from list because collateral was spent. " /* Continued */
468468
"collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
469469
__func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight,
470-
newList.GetAllMNsCount());
470+
newList.GetCounts().total());
471471
}
472472
}
473473
}

src/governance/governance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1466,7 +1466,7 @@ std::vector<std::shared_ptr<const CGovernanceObject>> CGovernanceManager::GetApp
14661466

14671467
// A proposal is considered passing if (YES votes) >= (Total Weight of Masternodes / 10),
14681468
// count total valid (ENABLED) masternodes to determine passing threshold.
1469-
const int nWeightedMnCount = tip_mn_list.GetValidWeightedMNsCount();
1469+
const int nWeightedMnCount = tip_mn_list.GetCounts().m_valid_weighted;
14701470
const int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nWeightedMnCount / 10);
14711471

14721472
LOCK(cs_store);

0 commit comments

Comments
 (0)