Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <governance/governance.h>
6 :
7 : #include <evo/deterministicmns.h>
8 : #include <flat-database.h>
9 : #include <governance/common.h>
10 : #include <governance/object.h>
11 : #include <governance/superblock.h>
12 : #include <masternode/meta.h>
13 : #include <masternode/sync.h>
14 :
15 : #include <chain.h>
16 : #include <chainparams.h>
17 : #include <common/bloom.h>
18 : #include <deploymentstatus.h>
19 : #include <net.h>
20 : #include <node/interface_ui.h>
21 : #include <protocol.h>
22 : #include <timedata.h>
23 : #include <util/check.h>
24 : #include <util/time.h>
25 : #include <validationinterface.h>
26 :
27 : #include <ranges>
28 :
29 3308 : const std::string GovernanceStore::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-16";
30 :
31 : namespace {
32 : constexpr std::chrono::seconds GOVERNANCE_DELETION_DELAY{10min};
33 : constexpr std::chrono::seconds GOVERNANCE_ORPHAN_EXPIRATION_TIME{10min};
34 : constexpr std::chrono::seconds MAX_TIME_FUTURE_DEVIATION{1h};
35 : constexpr std::chrono::seconds RELIABLE_PROPAGATION_TIME{1min};
36 :
37 : class ScopedLockBool
38 : {
39 : bool& ref;
40 : bool fPrevValue;
41 :
42 : public:
43 198680 : ScopedLockBool(Mutex& _cs, bool& _ref, bool _value) :
44 99340 : ref(_ref)
45 99340 : {
46 99340 : AssertLockHeld(_cs);
47 99340 : fPrevValue = ref;
48 99340 : ref = _value;
49 198680 : }
50 :
51 198680 : ~ScopedLockBool() { ref = fPrevValue; }
52 : };
53 : } // anonymous namespace
54 :
55 10467 : GovernanceStore::GovernanceStore() :
56 6751 : cs_store(),
57 6751 : mapObjects(),
58 6751 : mapErasedGovernanceObjects(),
59 6751 : cmapInvalidVotes(MAX_CACHE_SIZE),
60 6751 : cmmapOrphanVotes(MAX_CACHE_SIZE),
61 6751 : mapLastMasternodeObject(),
62 6751 : lastMNListForVotingKeys(std::make_shared<CDeterministicMNList>())
63 3716 : {
64 10467 : }
65 :
66 6070 : CGovernanceManager::CGovernanceManager(CMasternodeMetaMan& mn_metaman, const ChainstateManager& chainman,
67 : governance::SuperblockManager& superblocks, CDeterministicMNManager& dmnman,
68 : CMasternodeSync& mn_sync) :
69 3035 : m_db{std::make_unique<db_type>("governance.dat", "magicGovernanceCache")},
70 : m_mn_metaman{mn_metaman},
71 : m_chainman{chainman},
72 : m_superblocks{superblocks},
73 : m_dmnman{dmnman},
74 : m_mn_sync{mn_sync},
75 : cmapVoteToObject{MAX_CACHE_SIZE},
76 : mapPostponedObjects{}
77 3035 : {
78 3035 : }
79 :
80 6070 : CGovernanceManager::~CGovernanceManager()
81 3035 : {
82 : // Reset the paired SuperblockManager so its loaded-flag and trigger map don't
83 : // outlive us. chain_helper (its owner) must outlive govman -- see PrepareShutdown
84 : // in init.cpp and the test teardown ordering.
85 3035 : m_superblocks.Clear();
86 3035 : if (!is_loaded) return;
87 2831 : m_db->Store(*this);
88 6070 : }
89 :
90 2831 : bool CGovernanceManager::LoadCache(bool load_cache)
91 : {
92 2831 : AssertLockNotHeld(cs_store);
93 2831 : assert(m_db != nullptr);
94 2831 : is_loaded = load_cache ? m_db->Load(*this) : m_db->Store(*this);
95 2831 : if (is_loaded && load_cache) {
96 1946 : CheckAndRemove();
97 1946 : InitOnLoad();
98 1946 : }
99 2831 : m_superblocks.SetLoaded(is_loaded);
100 2831 : return is_loaded;
101 : }
102 :
103 300 : void CGovernanceManager::RelayObject(const CGovernanceObject& obj)
104 : {
105 300 : AssertLockNotHeld(cs_relay);
106 300 : if (!m_mn_sync.IsSynced()) {
107 8 : LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__);
108 8 : return;
109 : }
110 :
111 292 : LOCK(cs_relay);
112 292 : m_relay_invs.emplace_back(MSG_GOVERNANCE_OBJECT, obj.GetHash());
113 300 : }
114 :
115 1469 : void CGovernanceManager::RelayVote(const CGovernanceVote& vote)
116 : {
117 1469 : AssertLockNotHeld(cs_relay);
118 1469 : if (!m_mn_sync.IsSynced()) {
119 0 : LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__);
120 0 : return;
121 : }
122 :
123 1469 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
124 1469 : auto dmn = tip_mn_list.GetMNByCollateral(vote.GetMasternodeOutpoint());
125 1469 : if (!dmn) {
126 0 : return;
127 : }
128 :
129 1469 : LOCK(cs_relay);
130 1469 : m_relay_invs.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, vote.GetHash());
131 1469 : }
132 :
133 : // Accessors for thread-safe access to maps
134 248 : bool CGovernanceManager::HaveObjectForHash(const uint256& nHash) const
135 : {
136 248 : LOCK(cs_store);
137 248 : return (mapObjects.count(nHash) == 1 || mapPostponedObjects.count(nHash) == 1);
138 248 : }
139 :
140 248 : bool CGovernanceManager::SerializeObjectForHash(const uint256& nHash, CDataStream& ss) const
141 : {
142 248 : LOCK(cs_store);
143 248 : auto it = mapObjects.find(nHash);
144 248 : if (it == mapObjects.end()) {
145 0 : it = mapPostponedObjects.find(nHash);
146 0 : if (it == mapPostponedObjects.end())
147 0 : return false;
148 0 : }
149 248 : ss << it->second;
150 248 : return true;
151 248 : }
152 :
153 1224 : bool CGovernanceManager::HaveVoteForHash(const uint256& nHash) const
154 : {
155 1224 : LOCK(cs_store);
156 :
157 1224 : std::shared_ptr<CGovernanceObject> pGovobj{nullptr};
158 2448 : return cmapVoteToObject.Get(nHash, pGovobj) && WITH_LOCK(pGovobj->cs, return pGovobj->GetVoteFile().HasVote(nHash));
159 1224 : }
160 :
161 136 : int CGovernanceManager::GetVoteCount() const
162 : {
163 136 : LOCK(cs_store);
164 136 : return (int)cmapVoteToObject.GetSize();
165 136 : }
166 :
167 1224 : bool CGovernanceManager::SerializeVoteForHash(const uint256& nHash, CDataStream& ss) const
168 : {
169 1224 : LOCK(cs_store);
170 :
171 1224 : std::shared_ptr<CGovernanceObject> pGovobj{nullptr};
172 2448 : return cmapVoteToObject.Get(nHash, pGovobj) && WITH_LOCK(pGovobj->cs, return pGovobj->GetVoteFile().SerializeVoteToStream(nHash, ss));
173 1224 : }
174 :
175 0 : void CGovernanceManager::AddPostponedObject(const CGovernanceObject& govobj)
176 : {
177 0 : LOCK(cs_store);
178 0 : AddPostponedObjectInternal(govobj);
179 0 : }
180 :
181 0 : void CGovernanceManager::AddPostponedObjectInternal(const CGovernanceObject& govobj)
182 : {
183 0 : AssertLockHeld(cs_store);
184 0 : mapPostponedObjects.emplace(govobj.GetHash(), std::make_shared<CGovernanceObject>(govobj));
185 0 : }
186 :
187 248 : bool CGovernanceManager::ProcessObject(const std::string& peer_str, const uint256& nHash, CGovernanceObject& govobj)
188 : {
189 248 : std::string strHash = nHash.ToString();
190 :
191 248 : LOCK(cs_store);
192 :
193 248 : if (mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || mapErasedGovernanceObjects.count(nHash)) {
194 : // TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE?
195 0 : LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash);
196 0 : return true;
197 : }
198 :
199 248 : bool fRateCheckBypassed = false;
200 248 : if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) {
201 0 : LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n",
202 : strHash, nCachedBlockHeight);
203 0 : return true;
204 : }
205 :
206 248 : std::string strError;
207 : // CHECK OBJECT AGAINST LOCAL BLOCKCHAIN
208 :
209 248 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
210 248 : bool fMissingConfirmations = false;
211 248 : bool fIsValid = govobj.IsValidLocally(tip_mn_list, m_chainman, strError, fMissingConfirmations, true);
212 :
213 : bool unused_rcb;
214 248 : if (fRateCheckBypassed && fIsValid && !MasternodeRateCheck(govobj, true, true, unused_rcb)) {
215 0 : LogPrint(BCLog::GOBJECT, /* Continued */
216 : "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current "
217 : "block height %d)\n",
218 : strHash, nCachedBlockHeight);
219 0 : return true;
220 : }
221 :
222 248 : if (!fIsValid) {
223 0 : if (fMissingConfirmations) {
224 0 : AddPostponedObjectInternal(govobj);
225 0 : LogPrintf("MNGOVERNANCEOBJECT -- Not enough fee confirmations for: %s, strError = %s\n", strHash, strError);
226 0 : return true;
227 : } else {
228 0 : LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Governance object is invalid - %s\n", strError);
229 0 : return false;
230 : }
231 : }
232 :
233 248 : AddGovernanceObjectInternal(govobj, peer_str);
234 248 : return true;
235 248 : }
236 :
237 300 : void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj)
238 : {
239 300 : AssertLockHeld(cs_store);
240 300 : AssertLockNotHeld(cs_relay);
241 :
242 300 : uint256 nHash = govobj.GetHash();
243 300 : std::vector<vote_time_pair_t> vecVotePairs;
244 300 : cmmapOrphanVotes.GetAll(nHash, vecVotePairs);
245 :
246 300 : ScopedLockBool guard(cs_store, fRateChecksEnabled, false);
247 :
248 300 : int64_t nNow = GetAdjustedTime();
249 300 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
250 300 : for (const auto& pairVote : vecVotePairs) {
251 0 : const auto& [vote, time] = pairVote;
252 0 : bool fRemove = false;
253 0 : CGovernanceException e;
254 0 : if (time < nNow) {
255 0 : fRemove = true;
256 0 : } else if (govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, tip_mn_list, vote, e)) {
257 0 : RelayVote(vote);
258 0 : fRemove = true;
259 0 : }
260 0 : if (fRemove) {
261 0 : cmmapOrphanVotes.Erase(nHash, pairVote);
262 0 : }
263 0 : }
264 300 : }
265 :
266 300 : void CGovernanceManager::AddGovernanceObjectInternal(CGovernanceObject& insert_obj, const std::string& peer_str)
267 : {
268 300 : AssertLockHeld(::cs_main);
269 300 : AssertLockHeld(cs_store);
270 300 : AssertLockNotHeld(cs_relay);
271 :
272 300 : uint256 nHash = insert_obj.GetHash();
273 300 : std::string strHash = nHash.ToString();
274 :
275 300 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
276 :
277 : // UPDATE CACHED VARIABLES FOR THIS OBJECT AND ADD IT TO OUR MANAGED DATA
278 :
279 300 : insert_obj.UpdateSentinelVariables(tip_mn_list); //this sets local vars in object
280 :
281 300 : std::string strError;
282 :
283 : // MAKE SURE THIS OBJECT IS OK
284 :
285 300 : if (!insert_obj.IsValidLocally(tip_mn_list, m_chainman, strError, true)) {
286 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- invalid governance object - %s - (nCachedBlockHeight %d) \n", strError, nCachedBlockHeight);
287 0 : return;
288 : }
289 :
290 300 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- Adding object: hash = %s, type = %d\n", nHash.ToString(),
291 : std23::to_underlying(insert_obj.GetObjectType()));
292 :
293 : // INSERT INTO OUR GOVERNANCE OBJECT MEMORY
294 : // IF WE HAVE THIS OBJECT ALREADY, WE DON'T WANT ANOTHER COPY
295 300 : auto [emplace_ret, emplace_status] = mapObjects.emplace(nHash, std::make_shared<CGovernanceObject>(insert_obj));
296 :
297 300 : if (!emplace_status) {
298 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- already have governance object %s\n", nHash.ToString());
299 0 : return;
300 : }
301 :
302 : // SHOULD WE ADD THIS OBJECT TO ANY OTHER MANAGERS?
303 :
304 2288 : auto& [_, govobj] = *emplace_ret;
305 300 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- Before trigger block, GetDataAsPlainString = %s, nObjectType = %d\n",
306 : Assert(govobj)->GetDataAsPlainString(), std23::to_underlying(govobj->GetObjectType()));
307 :
308 300 : if (govobj->GetObjectType() == GovernanceObject::TRIGGER && !m_superblocks.AddTrigger(govobj, nCachedBlockHeight)) {
309 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- undo adding invalid trigger object: hash = %s\n", nHash.ToString());
310 0 : govobj->PrepareDeletion(GetTime<std::chrono::seconds>().count());
311 0 : return;
312 : }
313 :
314 300 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- %s new, received from peer %s\n", strHash, peer_str);
315 300 : RelayObject(*govobj);
316 :
317 : // Update the rate buffer
318 300 : MasternodeRateUpdate(*govobj);
319 :
320 300 : m_mn_sync.BumpAssetLastTime("CGovernanceManager::AddGovernanceObject");
321 :
322 : // WE MIGHT HAVE PENDING/ORPHAN VOTES FOR THIS OBJECT
323 :
324 300 : CheckOrphanVotes(*govobj);
325 :
326 : // SEND NOTIFICATION TO SCRIPT/ZMQ
327 300 : GetMainSignals().NotifyGovernanceObject(std::make_shared<const Governance::Object>(govobj->Object()), nHash.ToString());
328 300 : uiInterface.NotifyGovernanceChanged();
329 300 : }
330 :
331 52 : void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, const std::string& peer_str)
332 : {
333 52 : LOCK2(::cs_main, cs_store);
334 52 : AddGovernanceObjectInternal(govobj, peer_str);
335 52 : }
336 :
337 125597 : void CGovernanceManager::CheckAndRemove()
338 : {
339 125597 : AssertLockNotHeld(cs_store);
340 125597 : assert(m_mn_metaman.IsValid());
341 :
342 : // Return on initial sync, spammed the debug.log and provided no use
343 125597 : if (!m_mn_sync.IsBlockchainSynced()) return;
344 :
345 119748 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean\n");
346 :
347 119748 : std::vector<uint256> vecDirtyHashes = m_mn_metaman.GetAndClearDirtyGovernanceObjectHashes();
348 :
349 119748 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
350 :
351 : {
352 119748 : LOCK2(::cs_main, cs_store);
353 :
354 119748 : for (const uint256& nHash : vecDirtyHashes) {
355 20708 : auto it = mapObjects.find(nHash);
356 0 : if (it == mapObjects.end()) {
357 0 : continue;
358 : }
359 0 : Assert(it->second)->ClearMasternodeVotes(tip_mn_list);
360 : }
361 :
362 99040 : ScopedLockBool guard(cs_store, fRateChecksEnabled, false);
363 :
364 : // Clean up any expired or invalid triggers
365 119748 : m_superblocks.Clean(nCachedBlockHeight);
366 :
367 99040 : const auto nNow = GetTime<std::chrono::seconds>();
368 111966 : for (auto it = mapObjects.begin(); it != mapObjects.end();) {
369 84822 : auto [nHash, pObj] = *it;
370 33634 : std::string strHash = nHash.ToString();
371 :
372 : // IF CACHE IS NOT DIRTY, WHY DO THIS?
373 33634 : if (Assert(pObj)->IsSetDirtyCache()) {
374 : // UPDATE LOCAL VALIDITY AGAINST CRYPTO DATA
375 592 : pObj->UpdateLocalValidity(tip_mn_list, m_chainman);
376 :
377 : // UPDATE SENTINEL SIGNALING VARIABLES
378 592 : pObj->UpdateSentinelVariables(tip_mn_list);
379 592 : }
380 :
381 : // IF DELETE=TRUE, THEN CLEAN THE MESS UP!
382 :
383 12926 : const auto nTimeSinceDeletion = nNow - std::chrono::seconds{pObj->GetDeletionTime()};
384 :
385 12926 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- Checking object for deletion: %s, deletion time = %d, time since deletion = %d, delete flag = %d, expired flag = %d\n",
386 : strHash, pObj->GetDeletionTime(), nTimeSinceDeletion.count(), pObj->IsSetCachedDelete(), pObj->IsSetExpired());
387 :
388 15498 : if ((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) &&
389 12926 : (nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) {
390 60 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- erase obj %s\n", nHash.ToString());
391 120 : m_mn_metaman.RemoveGovernanceObject(pObj->GetHash());
392 :
393 : // Remove vote references
394 60 : const object_ref_cm_t::list_t& listItems = cmapVoteToObject.GetItemList();
395 600 : for (auto lit = listItems.begin(); lit != listItems.end();) {
396 540 : if (lit->value == pObj) {
397 280 : uint256 nKey = lit->key;
398 280 : ++lit;
399 280 : cmapVoteToObject.Erase(nKey);
400 280 : } else {
401 260 : ++lit;
402 : }
403 : }
404 :
405 60 : int64_t nTimeExpired{0};
406 :
407 60 : if (pObj->GetObjectType() == GovernanceObject::PROPOSAL) {
408 : // keep hashes of deleted proposals forever
409 40 : nTimeExpired = std::numeric_limits<int64_t>::max();
410 40 : } else {
411 20 : int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
412 60 : nTimeExpired = (std::chrono::seconds{pObj->GetCreationTime()} +
413 40 : std::chrono::seconds{2 * nSuperblockCycleSeconds} + GOVERNANCE_DELETION_DELAY)
414 20 : .count();
415 : }
416 :
417 120 : mapErasedGovernanceObjects.insert(std::make_pair(nHash, nTimeExpired));
418 60 : if (pObj->GetObjectType() == GovernanceObject::TRIGGER) {
419 40 : m_superblocks.RemoveTrigger(nHash);
420 20 : }
421 60 : mapObjects.erase(it++);
422 60 : } else {
423 2512 : if (pObj->GetObjectType() == GovernanceObject::PROPOSAL) {
424 5616 : std::string strValidationError;
425 5616 : if (!governance::ValidateProposal(pObj->GetDataAsHexString(), strValidationError)) {
426 238 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- set for deletion expired obj %s\n", strHash);
427 238 : pObj->PrepareDeletion(nNow.count());
428 238 : }
429 5616 : }
430 12866 : ++it;
431 : }
432 12926 : }
433 :
434 : // forget about expired deleted objects
435 100094 : for (auto s_it = mapErasedGovernanceObjects.begin(); s_it != mapErasedGovernanceObjects.end();) {
436 1054 : if (s_it->second < nNow.count()) {
437 0 : mapErasedGovernanceObjects.erase(s_it++);
438 0 : } else {
439 1054 : ++s_it;
440 : }
441 : }
442 :
443 : // forget about expired requests
444 99690 : for (auto r_it = m_requested_hash_time.begin(); r_it != m_requested_hash_time.end();) {
445 650 : if (r_it->second < nNow) {
446 6 : m_requested_hash_time.erase(r_it++);
447 6 : } else {
448 644 : ++r_it;
449 : }
450 : }
451 99040 : }
452 :
453 99040 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- %s, m_requested_hash_time size=%d\n",
454 : ToString(), m_requested_hash_time.size());
455 311969 : }
456 :
457 95252 : std::vector<CInv> CGovernanceManager::FetchRelayInventory()
458 : {
459 95252 : std::vector<CInv> ret;
460 95252 : LOCK(cs_relay);
461 95252 : swap(ret, m_relay_invs);
462 95252 : return ret;
463 95252 : }
464 :
465 210 : std::shared_ptr<const CGovernanceObject> CGovernanceManager::FindConstGovernanceObject(const uint256& nHash) const
466 : {
467 210 : LOCK(cs_store);
468 210 : return FindConstGovernanceObjectInternal(nHash);
469 210 : }
470 :
471 834 : std::shared_ptr<const CGovernanceObject> CGovernanceManager::FindConstGovernanceObjectInternal(const uint256& nHash) const
472 : {
473 834 : AssertLockHeld(cs_store);
474 :
475 834 : auto it = mapObjects.find(nHash);
476 834 : if (it != mapObjects.end()) return (it->second);
477 :
478 0 : return nullptr;
479 834 : }
480 :
481 3836 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObject(const uint256& nHash)
482 : {
483 3836 : AssertLockNotHeld(cs_store);
484 3836 : LOCK(cs_store);
485 3836 : return FindGovernanceObjectInternal(nHash);
486 3836 : }
487 :
488 3836 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObjectInternal(const uint256& nHash)
489 : {
490 3836 : AssertLockHeld(cs_store);
491 3836 : if (mapObjects.count(nHash)) return mapObjects[nHash];
492 0 : return nullptr;
493 3836 : }
494 :
495 204 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObjectByDataHash(const uint256 &nDataHash)
496 : {
497 204 : AssertLockNotHeld(cs_store);
498 204 : LOCK(cs_store);
499 1184 : for (const auto& [nHash, govobj] : mapObjects) {
500 980 : if (Assert(govobj)->GetDataHash() == nDataHash) return govobj;
501 : }
502 150 : return nullptr;
503 204 : }
504 :
505 0 : std::vector<CGovernanceVote> CGovernanceManager::GetCurrentVotes(const uint256& nParentHash, const COutPoint& mnCollateralOutpointFilter) const
506 : {
507 0 : LOCK(cs_store);
508 0 : std::vector<CGovernanceVote> vecResult;
509 :
510 : // Find the governance object or short-circuit.
511 0 : auto it = mapObjects.find(nParentHash);
512 0 : if (it == mapObjects.end()) return vecResult;
513 0 : const auto& govobj = *Assert(it->second);
514 :
515 0 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
516 0 : std::map<COutPoint, CDeterministicMNCPtr> mapMasternodes;
517 0 : if (mnCollateralOutpointFilter.IsNull()) {
518 0 : tip_mn_list.ForEachMNShared(/*onlyValid=*/false,
519 0 : [&](const auto& dmn) { mapMasternodes.emplace(dmn->collateralOutpoint, dmn); });
520 0 : } else {
521 0 : auto dmn = tip_mn_list.GetMNByCollateral(mnCollateralOutpointFilter);
522 0 : if (dmn) {
523 0 : mapMasternodes.emplace(dmn->collateralOutpoint, dmn);
524 0 : }
525 0 : }
526 :
527 : // Loop through each MN collateral outpoint and get the votes for the `nParentHash` governance object
528 0 : for (const auto& [outpoint, _] : mapMasternodes) {
529 : // get a vote_rec_t from the govobj
530 0 : vote_rec_t voteRecord;
531 0 : if (!govobj.GetCurrentMNVotes(outpoint, voteRecord)) continue;
532 :
533 0 : for (const auto& [signal, vote_instance] : voteRecord.mapInstances) {
534 0 : CGovernanceVote vote = CGovernanceVote(outpoint, nParentHash, (vote_signal_enum_t)signal,
535 0 : vote_instance.eOutcome);
536 0 : vote.SetTime(vote_instance.nCreationTime);
537 0 : vecResult.push_back(vote);
538 0 : }
539 0 : }
540 :
541 0 : return vecResult;
542 0 : }
543 :
544 4498 : void CGovernanceManager::GetAllNewerThan(std::vector<CGovernanceObject>& objs, int64_t nMoreThanTime,
545 : bool include_postponed) const
546 : {
547 4498 : LOCK(cs_store);
548 :
549 35438 : for (const auto& [_, govobj] : mapObjects) {
550 30940 : if (Assert(govobj)->GetCreationTime() < nMoreThanTime) {
551 0 : continue;
552 : }
553 61880 : objs.push_back(*govobj);
554 : }
555 :
556 4498 : if (include_postponed) {
557 0 : for (const auto& [_, govobj] : mapPostponedObjects) {
558 0 : if (Assert(govobj)->GetCreationTime() < nMoreThanTime) {
559 0 : continue;
560 : }
561 0 : objs.push_back(*govobj);
562 : }
563 0 : }
564 4498 : }
565 :
566 3478 : bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv)
567 : {
568 3478 : AssertLockNotHeld(cs_store);
569 :
570 : // do not request objects until it's time to sync
571 3478 : if (!m_mn_sync.IsBlockchainSynced()) return false;
572 :
573 3478 : LOCK(cs_store);
574 :
575 3478 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest inv = %s\n", inv.ToString());
576 :
577 : // First check if we've already recorded this object
578 3478 : switch (inv.type) {
579 : case MSG_GOVERNANCE_OBJECT: {
580 605 : if (mapObjects.count(inv.hash) == 1 || mapPostponedObjects.count(inv.hash) == 1) {
581 99 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest already have governance object, returning false\n");
582 99 : return false;
583 : }
584 506 : break;
585 : }
586 : case MSG_GOVERNANCE_OBJECT_VOTE: {
587 2873 : if (cmapVoteToObject.HasKey(inv.hash)) {
588 413 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest already have governance vote, returning false\n");
589 413 : return false;
590 : }
591 2460 : break;
592 : }
593 : default:
594 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest unknown type, returning false\n");
595 0 : return false;
596 : }
597 :
598 2966 : const auto valid_until = GetTime<std::chrono::seconds>() + RELIABLE_PROPAGATION_TIME;
599 2966 : const auto& [_itr, inserted] = m_requested_hash_time.emplace(inv.hash, valid_until);
600 :
601 2966 : if (inserted) {
602 1480 : LogPrint(BCLog::GOBJECT, /* Continued */
603 : "CGovernanceManager::ConfirmInventoryRequest added %s inv hash to m_requested_hash_time, size=%d\n",
604 : inv.type == MSG_GOVERNANCE_OBJECT ? "object" : "vote", m_requested_hash_time.size());
605 1480 : }
606 :
607 2966 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest reached end, returning true\n");
608 2966 : return true;
609 3478 : }
610 :
611 606 : std::vector<CInv> CGovernanceManager::GetSyncableVoteInvs(const uint256& nProp, const CBloomFilter& filter) const
612 : {
613 606 : LOCK(cs_store);
614 :
615 606 : auto it = mapObjects.find(nProp);
616 606 : if (it == mapObjects.end()) {
617 54 : return {};
618 : }
619 :
620 552 : const auto& govobj = *Assert(it->second);
621 552 : if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) {
622 30 : return {};
623 : }
624 :
625 522 : std::vector<CInv> invs;
626 522 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
627 :
628 522 : LOCK(govobj.cs);
629 522 : const auto& fileVotes = govobj.GetVoteFile();
630 2774 : for (const auto& vote : fileVotes.GetVotes()) {
631 2252 : uint256 nVoteHash = vote.GetHash();
632 :
633 2252 : bool onlyVotingKeyAllowed = govobj.GetObjectType() == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
634 :
635 2252 : if (filter.contains(nVoteHash) || !vote.IsValid(tip_mn_list, onlyVotingKeyAllowed)) {
636 2220 : continue;
637 : }
638 32 : invs.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash);
639 : }
640 :
641 522 : return invs;
642 1128 : }
643 :
644 88 : std::vector<CInv> CGovernanceManager::GetSyncableObjectInvs() const
645 : {
646 88 : LOCK(cs_store);
647 :
648 88 : std::vector<CInv> invs;
649 88 : invs.reserve(mapObjects.size());
650 :
651 448 : for (const auto& [nHash, govobj] : mapObjects) {
652 180 : if (Assert(govobj)->IsSetCachedDelete() || govobj->IsSetExpired()) {
653 0 : continue;
654 : }
655 360 : invs.emplace_back(MSG_GOVERNANCE_OBJECT, nHash);
656 : }
657 :
658 88 : return invs;
659 88 : }
660 :
661 300 : void CGovernanceManager::MasternodeRateUpdate(const CGovernanceObject& govobj)
662 : {
663 300 : AssertLockHeld(cs_store);
664 :
665 300 : if (govobj.GetObjectType() != GovernanceObject::TRIGGER) return;
666 :
667 188 : const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
668 188 : auto it = mapLastMasternodeObject.find(masternodeOutpoint);
669 :
670 188 : if (it == mapLastMasternodeObject.end()) {
671 86 : it = mapLastMasternodeObject.insert(txout_m_t::value_type(masternodeOutpoint, last_object_rec(true))).first;
672 86 : }
673 :
674 188 : int64_t nTimestamp = govobj.GetCreationTime();
675 188 : it->second.triggerBuffer.AddTimestamp(nTimestamp);
676 :
677 188 : if (nTimestamp > GetTime() + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME)) {
678 : // schedule additional relay for the object
679 0 : setAdditionalRelayObjects.insert(govobj.GetHash());
680 0 : }
681 :
682 188 : it->second.fStatusOK = true;
683 300 : }
684 :
685 52 : bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus)
686 : {
687 52 : LOCK(cs_store);
688 : bool fRateCheckBypassed;
689 52 : return MasternodeRateCheck(govobj, fUpdateFailStatus, true, fRateCheckBypassed);
690 52 : }
691 :
692 385 : bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus, bool fForce, bool& fRateCheckBypassed)
693 : {
694 385 : AssertLockHeld(cs_store);
695 :
696 385 : fRateCheckBypassed = false;
697 :
698 385 : if (!m_mn_sync.IsSynced() || !fRateChecksEnabled) {
699 8 : return true;
700 : }
701 :
702 377 : if (govobj.GetObjectType() != GovernanceObject::TRIGGER) {
703 112 : return true;
704 : }
705 :
706 265 : const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
707 265 : int64_t nTimestamp = govobj.GetCreationTime();
708 265 : int64_t nNow = GetAdjustedTime();
709 265 : int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
710 :
711 265 : std::string strHash = govobj.GetHash().ToString();
712 :
713 265 : if (nTimestamp < nNow - 2 * nSuperblockCycleSeconds) {
714 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too old timestamp, masternode = %s, timestamp = %d, current time = %d\n",
715 : strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow);
716 0 : return false;
717 : }
718 :
719 265 : if (nTimestamp > nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION)) {
720 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too new (future) timestamp, masternode = %s, timestamp = %d, current time = %d\n",
721 : strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow);
722 0 : return false;
723 : }
724 :
725 265 : auto it = mapLastMasternodeObject.find(masternodeOutpoint);
726 265 : if (it == mapLastMasternodeObject.end()) return true;
727 :
728 187 : if (it->second.fStatusOK && !fForce) {
729 85 : fRateCheckBypassed = true;
730 85 : return true;
731 : }
732 :
733 : // Allow 1 trigger per mn per cycle, with a small fudge factor
734 102 : double dMaxRate = 2 * 1.1 / double(nSuperblockCycleSeconds);
735 :
736 : // Temporary copy to check rate after new timestamp is added
737 102 : CRateCheckBuffer buffer = it->second.triggerBuffer;
738 :
739 102 : buffer.AddTimestamp(nTimestamp);
740 102 : double dRate = buffer.GetRate();
741 :
742 102 : if (dRate < dMaxRate) {
743 102 : return true;
744 : }
745 :
746 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- Rate too high: object hash = %s, masternode = %s, object timestamp = %d, rate = %f, max rate = %f\n",
747 : strHash, masternodeOutpoint.ToStringShort(), nTimestamp, dRate, dMaxRate);
748 :
749 0 : if (fUpdateFailStatus) {
750 0 : it->second.fStatusOK = false;
751 0 : }
752 :
753 0 : return false;
754 385 : }
755 :
756 276 : bool CGovernanceManager::ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
757 : {
758 276 : AssertLockNotHeld(cs_store);
759 276 : AssertLockNotHeld(cs_relay);
760 276 : uint256 hashToRequest; // Ignored for local votes (no peer to request from)
761 276 : bool fOK = ProcessVote(vote, exception, hashToRequest);
762 276 : if (fOK) {
763 256 : RelayVote(vote);
764 256 : }
765 276 : return fOK;
766 : }
767 :
768 1500 : bool CGovernanceManager::ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception,
769 : uint256& hashToRequest)
770 : {
771 1500 : AssertLockNotHeld(cs_store);
772 1500 : hashToRequest = uint256{};
773 :
774 1500 : LOCK(cs_store);
775 1500 : uint256 nHashVote = vote.GetHash();
776 1500 : uint256 nHashGovobj = vote.GetParentHash();
777 :
778 1500 : if (cmapVoteToObject.HasKey(nHashVote)) {
779 0 : LogPrint(BCLog::GOBJECT, "CGovernanceObject::%s -- skipping known valid vote %s for object %s\n", __func__,
780 : nHashVote.ToString(), nHashGovobj.ToString());
781 0 : return false;
782 : }
783 :
784 1500 : if (cmapInvalidVotes.HasKey(nHashVote)) {
785 0 : std::string msg{strprintf("CGovernanceManager::%s -- Old invalid vote, MN outpoint = %s, governance object hash = %s",
786 0 : __func__, vote.GetMasternodeOutpoint().ToStringShort(), nHashGovobj.ToString())};
787 0 : LogPrint(BCLog::GOBJECT, "%s\n", msg);
788 0 : exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
789 0 : return false;
790 0 : }
791 :
792 1500 : auto it = mapObjects.find(nHashGovobj);
793 1500 : if (it == mapObjects.end()) {
794 0 : std::string msg{strprintf("CGovernanceManager::%s -- Unknown parent object %s, MN outpoint = %s", __func__,
795 0 : nHashGovobj.ToString(), vote.GetMasternodeOutpoint().ToStringShort())};
796 0 : exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_WARNING);
797 0 : if (cmmapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, count_seconds(GetTime<std::chrono::seconds>() +
798 : GOVERNANCE_ORPHAN_EXPIRATION_TIME)))) {
799 0 : hashToRequest = nHashGovobj; // Caller should request this object
800 0 : }
801 0 : LogPrint(BCLog::GOBJECT, "%s\n", msg);
802 0 : return false;
803 0 : }
804 :
805 1500 : auto& govobj = *Assert(it->second);
806 :
807 1500 : if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) {
808 0 : LogPrint(BCLog::GOBJECT, "CGovernanceObject::%s -- ignoring vote for expired or deleted object, hash = %s\n",
809 : __func__, nHashGovobj.ToString());
810 0 : return false;
811 : }
812 :
813 1500 : bool fOk = govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, m_dmnman.GetListAtChainTip(), vote, exception);
814 1500 : if (fOk) {
815 1480 : fOk = cmapVoteToObject.Insert(nHashVote, it->second);
816 1500 : } else if (exception.GetType() == GOVERNANCE_EXCEPTION_PERMANENT_ERROR && exception.GetNodePenalty() == 20) {
817 0 : cmapInvalidVotes.Insert(nHashVote, vote);
818 0 : }
819 1500 : return fOk;
820 1500 : }
821 :
822 206314 : void CGovernanceManager::CheckPostponedObjects()
823 : {
824 206314 : AssertLockHeld(::cs_main);
825 206314 : AssertLockHeld(cs_store);
826 206314 : AssertLockNotHeld(cs_relay);
827 206314 : if (!m_mn_sync.IsSynced()) return;
828 :
829 : // Check postponed proposals
830 99244 : for (auto it = mapPostponedObjects.begin(); it != mapPostponedObjects.end();) {
831 0 : const uint256& nHash = it->first;
832 0 : auto& govobj = *Assert(it->second);
833 :
834 0 : assert(govobj.GetObjectType() != GovernanceObject::TRIGGER);
835 :
836 0 : std::string strError;
837 : bool fMissingConfirmations;
838 0 : if (govobj.IsCollateralValid(m_chainman, strError, fMissingConfirmations)) {
839 0 : if (govobj.IsValidLocally(m_dmnman.GetListAtChainTip(), m_chainman, strError, false)) {
840 0 : AddGovernanceObjectInternal(govobj, "<postponed>");
841 0 : } else {
842 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::CheckPostponedObjects -- %s invalid\n", nHash.ToString());
843 : }
844 :
845 0 : } else if (fMissingConfirmations) {
846 : // wait for more confirmations
847 0 : ++it;
848 0 : continue;
849 : }
850 :
851 : // remove processed or invalid object from the queue
852 0 : mapPostponedObjects.erase(it++);
853 0 : }
854 :
855 :
856 : // Perform additional relays for triggers
857 99244 : int64_t nNow = GetAdjustedTime();
858 99244 : int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
859 :
860 99244 : for (auto it = setAdditionalRelayObjects.begin(); it != setAdditionalRelayObjects.end();) {
861 0 : auto itObject = mapObjects.find(*it);
862 0 : if (itObject != mapObjects.end()) {
863 0 : const auto& govobj = *Assert(itObject->second);
864 :
865 0 : int64_t nTimestamp = govobj.GetCreationTime();
866 :
867 0 : bool fValid = (nTimestamp <= nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION)) &&
868 0 : (nTimestamp >= nNow - 2 * nSuperblockCycleSeconds);
869 0 : bool fReady = (nTimestamp <=
870 0 : nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME));
871 :
872 0 : if (fValid) {
873 0 : if (fReady) {
874 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::CheckPostponedObjects -- additional relay: hash = %s\n", govobj.GetHash().ToString());
875 0 : RelayObject(govobj);
876 0 : } else {
877 0 : it++;
878 0 : continue;
879 : }
880 0 : }
881 :
882 0 : } else {
883 0 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::CheckPostponedObjects -- additional relay of unknown object: %s\n", it->ToString());
884 : }
885 :
886 0 : setAdditionalRelayObjects.erase(it++);
887 : }
888 206314 : }
889 :
890 624 : CBloomFilter CGovernanceManager::GetVoteBloomFilter(const uint256& nHash) const
891 : {
892 624 : LOCK(cs_store);
893 :
894 624 : auto pObj = FindConstGovernanceObjectInternal(nHash);
895 624 : if (!pObj) {
896 0 : return CBloomFilter{};
897 : }
898 :
899 624 : CBloomFilter filter(Params().GetConsensus().nGovernanceFilterElements, GOVERNANCE_FILTER_FP_RATE,
900 624 : GetRand<int>(/*nMax=*/999999), BLOOM_UPDATE_ALL);
901 :
902 1248 : std::vector<CGovernanceVote> vecVotes = WITH_LOCK(pObj->cs, return pObj->GetVoteFile().GetVotes());
903 3216 : for (const auto& vote : vecVotes) {
904 2592 : filter.insert(vote.GetHash());
905 : }
906 :
907 624 : return filter;
908 1248 : }
909 :
910 13591 : CDeterministicMNManager& CGovernanceManager::GetMNManager() { return m_dmnman; }
911 :
912 12367 : std::pair<std::vector<uint256>, std::vector<uint256>> CGovernanceManager::FetchGovernanceObjectVotes(
913 : size_t nPeersPerHashMax, int64_t nNow, std::map<uint256, std::map<CService, int64_t>>& mapAskedRecently) const
914 : {
915 12367 : std::vector<uint256> vTriggerObjHashes;
916 12367 : std::vector<uint256> vOtherObjHashes;
917 : {
918 12367 : LOCK(cs_store);
919 :
920 61374 : for (const auto& [nHash, govobj] : mapObjects) {
921 5697 : if (Assert(govobj)->IsSetCachedDelete()) continue;
922 11038 : if (mapAskedRecently.count(nHash)) {
923 21158 : for (auto it = mapAskedRecently[nHash].begin(); it != mapAskedRecently[nHash].end();) {
924 10480 : if (it->second < nNow) {
925 512 : mapAskedRecently[nHash].erase(it++);
926 256 : } else {
927 10224 : ++it;
928 : }
929 : }
930 10678 : if (mapAskedRecently[nHash].size() >= nPeersPerHashMax) continue;
931 5339 : }
932 :
933 5519 : if (govobj->GetObjectType() == GovernanceObject::TRIGGER) {
934 2233 : vTriggerObjHashes.push_back(nHash);
935 2233 : } else {
936 3286 : vOtherObjHashes.push_back(nHash);
937 : }
938 : }
939 12367 : }
940 12367 : return {vTriggerObjHashes, vOtherObjHashes};
941 12367 : }
942 :
943 1472 : bool CGovernanceManager::AcceptMessage(const uint256& nHash)
944 : {
945 1472 : AssertLockNotHeld(cs_store);
946 1472 : LOCK(cs_store);
947 1472 : auto it = m_requested_hash_time.find(nHash);
948 1472 : if (it == m_requested_hash_time.end()) {
949 : // We never requested this
950 0 : return false;
951 : }
952 : // Only accept one response
953 1472 : m_requested_hash_time.erase(it);
954 1472 : return true;
955 1472 : }
956 :
957 1946 : void CGovernanceManager::RebuildIndexes()
958 : {
959 1946 : AssertLockHeld(cs_store);
960 :
961 1946 : cmapVoteToObject.Clear();
962 1946 : for (auto& [_, govobj] : mapObjects) {
963 0 : assert(govobj);
964 0 : std::vector<CGovernanceVote> vecVotes = WITH_LOCK(govobj->cs, return govobj->GetVoteFile().GetVotes());
965 0 : for (const auto& vecVote : vecVotes) {
966 0 : cmapVoteToObject.Insert(vecVote.GetHash(), govobj);
967 : }
968 0 : }
969 1946 : }
970 :
971 1946 : void CGovernanceManager::InitOnLoad()
972 : {
973 : {
974 1946 : LOCK(cs_store);
975 1946 : const auto start{SteadyClock::now()};
976 1946 : LogPrintf("Preparing masternode indexes and governance triggers...\n");
977 1946 : RebuildIndexes();
978 :
979 1946 : const int64_t nNow = GetTime<std::chrono::seconds>().count();
980 1946 : for (auto& [_, govobj] : mapObjects) {
981 0 : if (Assert(govobj)->GetObjectType() != GovernanceObject::TRIGGER) continue;
982 0 : if (!m_superblocks.AddTrigger(govobj, nCachedBlockHeight)) {
983 0 : govobj->PrepareDeletion(nNow);
984 0 : }
985 : }
986 1946 : LogPrintf("Masternode indexes and governance triggers prepared %dms\n",
987 : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
988 1946 : }
989 1946 : LogPrintf(" %s\n", ToString());
990 1946 : }
991 :
992 3410 : void GovernanceStore::Clear()
993 : {
994 3410 : LOCK(cs_store);
995 3410 : mapObjects.clear();
996 3410 : mapErasedGovernanceObjects.clear();
997 3410 : cmapInvalidVotes.Clear();
998 3410 : cmmapOrphanVotes.Clear();
999 3410 : mapLastMasternodeObject.clear();
1000 3410 : lastMNListForVotingKeys = std::make_shared<CDeterministicMNList>();
1001 3410 : }
1002 :
1003 0 : void CGovernanceManager::Clear()
1004 : {
1005 0 : AssertLockNotHeld(cs_store);
1006 0 : LogPrint(BCLog::GOBJECT, "Governance object manager was cleared\n");
1007 0 : GovernanceStore::Clear();
1008 0 : nTimeLastDiff = 0;
1009 0 : nCachedBlockHeight = 0;
1010 0 : cmapVoteToObject.Clear();
1011 0 : mapPostponedObjects.clear();
1012 0 : setAdditionalRelayObjects.clear();
1013 0 : m_requested_hash_time.clear();
1014 0 : fRateChecksEnabled = true;
1015 0 : m_superblocks.Clear();
1016 0 : }
1017 :
1018 108112 : std::string GovernanceStore::ToString() const
1019 : {
1020 108112 : LOCK(cs_store);
1021 :
1022 108112 : int nProposalCount = 0;
1023 108112 : int nTriggerCount = 0;
1024 108112 : int nOtherCount = 0;
1025 :
1026 121218 : for (const auto& [_, govobj] : mapObjects) {
1027 13106 : switch (Assert(govobj)->GetObjectType()) {
1028 : case GovernanceObject::PROPOSAL:
1029 5688 : nProposalCount++;
1030 5688 : break;
1031 : case GovernanceObject::TRIGGER:
1032 7418 : nTriggerCount++;
1033 7418 : break;
1034 : default:
1035 0 : nOtherCount++;
1036 0 : break;
1037 : }
1038 : }
1039 :
1040 108112 : return strprintf("Governance Objects: %d (Proposals: %d, Triggers: %d, Other: %d; Erased: %d)",
1041 108112 : (int)mapObjects.size(),
1042 108112 : nProposalCount, nTriggerCount, nOtherCount, (int)mapErasedGovernanceObjects.size());
1043 108112 : }
1044 :
1045 100986 : std::string CGovernanceManager::ToString() const
1046 : {
1047 100986 : AssertLockNotHeld(cs_store);
1048 100986 : return strprintf("%s, Votes: %d", GovernanceStore::ToString(), (int)cmapVoteToObject.GetSize());
1049 0 : }
1050 :
1051 206314 : void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex)
1052 : {
1053 206314 : AssertLockNotHeld(cs_store);
1054 206314 : AssertLockNotHeld(cs_relay);
1055 : // Note this gets called from ActivateBestChain without cs_main being held
1056 : // so it should be safe to lock our mutex here without risking a deadlock
1057 : // On the other hand it should be safe for us to access pindex without holding a lock
1058 : // on cs_main because the CBlockIndex objects are dynamically allocated and
1059 : // presumably never deleted.
1060 206314 : if (!pindex) {
1061 0 : return;
1062 : }
1063 :
1064 206314 : nCachedBlockHeight = pindex->nHeight;
1065 206314 : LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight);
1066 :
1067 206314 : LOCK2(::cs_main, cs_store);
1068 206314 : if (DeploymentDIP0003Enforced(pindex->nHeight, Params().GetConsensus())) {
1069 113460 : RemoveInvalidVotes();
1070 113460 : }
1071 :
1072 206314 : CheckPostponedObjects();
1073 :
1074 206314 : m_superblocks.ExecuteBestSuperblock(m_dmnman.GetListAtChainTip(), pindex->nHeight);
1075 206314 : }
1076 :
1077 3202 : std::vector<uint256> CGovernanceManager::GetOrphanVoteObjectHashes()
1078 : {
1079 3202 : LOCK(cs_store);
1080 :
1081 3202 : int64_t nNow = GetTime<std::chrono::seconds>().count();
1082 :
1083 : // Clean up expired orphan votes
1084 3202 : const vote_cmm_t::list_t& items = cmmapOrphanVotes.GetItemList();
1085 3202 : for (auto it = items.begin(); it != items.end();) {
1086 0 : auto prevIt = it;
1087 0 : ++it;
1088 0 : const auto& [_, time] = prevIt->value;
1089 0 : if (time < nNow) {
1090 0 : cmmapOrphanVotes.Erase(prevIt->key, prevIt->value);
1091 0 : }
1092 : }
1093 :
1094 : // Get hashes of objects we don't have yet
1095 3202 : std::vector<uint256> vecHashesFiltered;
1096 3202 : std::vector<uint256> vecHashes;
1097 3202 : cmmapOrphanVotes.GetKeys(vecHashes);
1098 3202 : for (const uint256& nHash : vecHashes) {
1099 0 : if (mapObjects.find(nHash) == mapObjects.end()) {
1100 0 : vecHashesFiltered.push_back(nHash);
1101 0 : }
1102 : }
1103 :
1104 3202 : return vecHashesFiltered;
1105 3202 : }
1106 :
1107 113460 : void CGovernanceManager::RemoveInvalidVotes()
1108 : {
1109 113460 : AssertLockHeld(cs_store);
1110 :
1111 113460 : if (!m_mn_sync.IsSynced()) {
1112 22190 : return;
1113 : }
1114 :
1115 91270 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
1116 91270 : auto diff = lastMNListForVotingKeys->BuildDiff(tip_mn_list);
1117 :
1118 91270 : std::vector<COutPoint> changedKeyMNs;
1119 276798 : for (const auto& [internalId, pdmnState] : diff.updatedMNs) {
1120 185528 : auto oldDmn = lastMNListForVotingKeys->GetMNByInternalId(internalId);
1121 : // BuildDiff will construct itself with MNs that we already have knowledge
1122 : // of, meaning that fetch operations should never fail.
1123 92764 : assert(oldDmn);
1124 92764 : if ((pdmnState.fields & CDeterministicMNStateDiff::Field_keyIDVoting) && pdmnState.state.keyIDVoting != oldDmn->pdmnState->keyIDVoting) {
1125 52 : changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
1126 92764 : } else if ((pdmnState.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && pdmnState.state.pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
1127 54 : changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
1128 54 : }
1129 92764 : }
1130 91682 : for (const auto& id : diff.removedMns) {
1131 412 : auto oldDmn = lastMNListForVotingKeys->GetMNByInternalId(id);
1132 : // BuildDiff will construct itself with MNs that we already have knowledge
1133 : // of, meaning that fetch operations should never fail.
1134 412 : assert(oldDmn);
1135 412 : changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
1136 412 : }
1137 :
1138 91788 : for (const auto& outpoint : changedKeyMNs) {
1139 518 : for (auto& [_, govobj] : mapObjects) {
1140 0 : auto removed = Assert(govobj)->RemoveInvalidVotes(tip_mn_list, outpoint);
1141 0 : if (removed.empty()) {
1142 0 : continue;
1143 : }
1144 0 : for (auto& voteHash : removed) {
1145 0 : cmapVoteToObject.Erase(voteHash);
1146 0 : cmapInvalidVotes.Erase(voteHash);
1147 0 : cmmapOrphanVotes.Erase(voteHash);
1148 0 : m_requested_hash_time.erase(voteHash);
1149 : }
1150 0 : }
1151 : }
1152 :
1153 : // store current MN list for the next run so that we can determine which keys changed
1154 91270 : lastMNListForVotingKeys = std::make_shared<CDeterministicMNList>(tip_mn_list);
1155 113460 : }
1156 :
1157 39483 : std::vector<std::shared_ptr<const CGovernanceObject>> CGovernanceManager::GetApprovedProposals(
1158 : const CDeterministicMNList& tip_mn_list)
1159 : {
1160 39483 : AssertLockNotHeld(cs_store);
1161 :
1162 : // Use std::vector of std::shared_ptr<const CGovernanceObject> because CGovernanceObject doesn't support move operations (needed for sorting the vector later)
1163 39483 : std::vector<std::shared_ptr<const CGovernanceObject>> ret{};
1164 :
1165 : // A proposal is considered passing if (YES votes) >= (Total Weight of Masternodes / 10),
1166 : // count total valid (ENABLED) masternodes to determine passing threshold.
1167 39483 : const int nWeightedMnCount = tip_mn_list.GetCounts().m_valid_weighted;
1168 39483 : const int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nWeightedMnCount / 10);
1169 :
1170 39483 : LOCK(cs_store);
1171 41315 : for (const auto& [_, govobj] : mapObjects) {
1172 : // Skip all non-proposals objects
1173 1256 : if (Assert(govobj)->GetObjectType() != GovernanceObject::PROPOSAL) continue;
1174 : // Skip non-passing proposals
1175 576 : const int absYesCount = govobj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
1176 576 : if (absYesCount < nAbsVoteReq) continue;
1177 576 : ret.emplace_back(govobj);
1178 : }
1179 :
1180 : // Sort approved proposals by absolute Yes votes descending
1181 39976 : std::sort(ret.begin(), ret.end(), [&tip_mn_list](auto& lhs, auto& rhs) {
1182 493 : const auto lhs_yes = lhs->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
1183 493 : const auto rhs_yes = rhs->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
1184 493 : return lhs_yes == rhs_yes ? UintToArith256(lhs->GetHash()) > UintToArith256(rhs->GetHash()) : lhs_yes > rhs_yes;
1185 : });
1186 :
1187 39483 : return ret;
1188 39483 : }
|