LCOV - code coverage report
Current view: top level - src/governance - governance.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 95 98 96.9 %
Date: 2026-06-25 07:23:43 Functions: 34 43 79.1 %

          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             : #ifndef BITCOIN_GOVERNANCE_GOVERNANCE_H
       6             : #define BITCOIN_GOVERNANCE_GOVERNANCE_H
       7             : 
       8             : #include <cachemap.h>
       9             : #include <cachemultimap.h>
      10             : #include <primitives/transaction.h>
      11             : #include <sync.h>
      12             : 
      13             : #include <chrono>
      14             : #include <limits>
      15             : #include <map>
      16             : #include <memory>
      17             : #include <set>
      18             : #include <string>
      19             : #include <vector>
      20             : 
      21             : class CBloomFilter;
      22             : class CBlockIndex;
      23             : class CConnman;
      24             : class CDataStream;
      25             : class CDeterministicMNList;
      26             : class CDeterministicMNManager;
      27             : class ChainstateManager;
      28             : template<typename T>
      29             : class CFlatDB;
      30             : class CGovernanceException;
      31             : class CGovernanceObject;
      32             : class CGovernanceVote;
      33             : class CInv;
      34             : class CMasternodeMetaMan;
      35             : class CMasternodeSync;
      36             : class CService;
      37             : struct RPCResult;
      38             : class UniValue;
      39             : 
      40             : namespace governance {
      41             : class SuperblockManager;
      42             : } // namespace governance
      43             : 
      44             : using vote_time_pair_t = std::pair<CGovernanceVote, int64_t>;
      45             : 
      46             : static constexpr int RATE_BUFFER_SIZE = 5;
      47             : static constexpr bool DEFAULT_GOVERNANCE_ENABLE{true};
      48             : 
      49             : extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration)
      50             : 
      51             : class CRateCheckBuffer
      52             : {
      53             : private:
      54             :     std::vector<int64_t> vecTimestamps;
      55             : 
      56          88 :     int nDataStart{0};
      57          88 :     int nDataEnd{0};
      58          88 :     bool fBufferEmpty{true};
      59             : 
      60             : public:
      61         176 :     CRateCheckBuffer() :
      62          88 :         vecTimestamps(RATE_BUFFER_SIZE)
      63          88 :     {
      64         176 :     }
      65             : 
      66         306 :     void AddTimestamp(int64_t nTimestamp)
      67             :     {
      68         306 :         if ((nDataEnd == nDataStart) && !fBufferEmpty) {
      69             :             // Buffer full, discard 1st element
      70           6 :             nDataStart = (nDataStart + 1) % RATE_BUFFER_SIZE;
      71           6 :         }
      72         306 :         vecTimestamps[nDataEnd] = nTimestamp;
      73         306 :         nDataEnd = (nDataEnd + 1) % RATE_BUFFER_SIZE;
      74         306 :         fBufferEmpty = false;
      75         306 :     }
      76             : 
      77          21 :     int64_t GetMinTimestamp() const
      78             :     {
      79          21 :         int nIndex = nDataStart;
      80          21 :         int64_t nMin = std::numeric_limits<int64_t>::max();
      81          21 :         if (fBufferEmpty) {
      82           1 :             return nMin;
      83             :         }
      84          20 :         do {
      85          74 :             if (vecTimestamps[nIndex] < nMin) {
      86          20 :                 nMin = vecTimestamps[nIndex];
      87          20 :             }
      88          74 :             nIndex = (nIndex + 1) % RATE_BUFFER_SIZE;
      89          74 :         } while (nIndex != nDataEnd);
      90          20 :         return nMin;
      91          21 :     }
      92             : 
      93          20 :     int64_t GetMaxTimestamp() const
      94             :     {
      95          20 :         int nIndex = nDataStart;
      96          20 :         int64_t nMax = 0;
      97          20 :         if (fBufferEmpty) {
      98           1 :             return nMax;
      99             :         }
     100          19 :         do {
     101          73 :             if (vecTimestamps[nIndex] > nMax) {
     102          73 :                 nMax = vecTimestamps[nIndex];
     103          73 :             }
     104          73 :             nIndex = (nIndex + 1) % RATE_BUFFER_SIZE;
     105          73 :         } while (nIndex != nDataEnd);
     106          19 :         return nMax;
     107          20 :     }
     108             : 
     109         128 :     int GetCount() const
     110             :     {
     111         128 :         if (fBufferEmpty) {
     112           2 :             return 0;
     113             :         }
     114         126 :         if (nDataEnd > nDataStart) {
     115         116 :             return nDataEnd - nDataStart;
     116             :         }
     117          10 :         return RATE_BUFFER_SIZE - nDataStart + nDataEnd;
     118         128 :     }
     119             : 
     120         110 :     double GetRate() const
     121             :     {
     122         110 :         int nCount = GetCount();
     123         110 :         if (nCount < RATE_BUFFER_SIZE) {
     124         108 :             return 0.0;
     125             :         }
     126           2 :         int64_t nMin = GetMinTimestamp();
     127           2 :         int64_t nMax = GetMaxTimestamp();
     128           2 :         if (nMin == nMax) {
     129             :             // multiple objects with the same timestamp => infinite rate
     130           0 :             return 1.0e10;
     131             :         }
     132           2 :         return double(nCount) / double(nMax - nMin);
     133         110 :     }
     134             : 
     135         258 :     SERIALIZE_METHODS(CRateCheckBuffer, obj)
     136             :     {
     137          86 :         READWRITE(obj.vecTimestamps, obj.nDataStart, obj.nDataEnd, obj.fBufferEmpty);
     138          86 :     }
     139             : };
     140             : 
     141             : class GovernanceStore
     142             : {
     143             : protected:
     144             :     struct last_object_rec {
     145         172 :         explicit last_object_rec(bool fStatusOKIn = true) :
     146          86 :             triggerBuffer(),
     147          86 :             fStatusOK(fStatusOKIn)
     148          86 :         {
     149         172 :         }
     150             : 
     151         258 :         SERIALIZE_METHODS(last_object_rec, obj)
     152             :         {
     153          86 :             READWRITE(obj.triggerBuffer, obj.fStatusOK);
     154          86 :         }
     155             : 
     156             :         CRateCheckBuffer triggerBuffer;
     157             :         bool fStatusOK;
     158             :     };
     159             : 
     160             :     using txout_m_t = std::map<COutPoint, last_object_rec>;
     161             :     using vote_cmm_t = CacheMultiMap<uint256, vote_time_pair_t>;
     162             : 
     163             : protected:
     164             :     static constexpr int MAX_CACHE_SIZE = 1000000;
     165             :     static const std::string SERIALIZATION_VERSION_STRING;
     166             : 
     167             : protected:
     168             :     // critical section to protect the inner data structures
     169             :     mutable Mutex cs_store;
     170             : 
     171             :     // keep track of the scanning errors
     172             :     std::map<uint256, std::shared_ptr<CGovernanceObject>> mapObjects GUARDED_BY(cs_store);
     173             :     // mapErasedGovernanceObjects contains key-value pairs, where
     174             :     //   key   - governance object's hash
     175             :     //   value - expiration time for deleted objects
     176             :     std::map<uint256, int64_t> mapErasedGovernanceObjects GUARDED_BY(cs_store);
     177             :     CacheMap<uint256, CGovernanceVote> cmapInvalidVotes GUARDED_BY(cs_store);
     178             :     vote_cmm_t cmmapOrphanVotes GUARDED_BY(cs_store);
     179             :     txout_m_t mapLastMasternodeObject GUARDED_BY(cs_store);
     180             :     // used to check for changed voting keys
     181             :     std::shared_ptr<CDeterministicMNList> lastMNListForVotingKeys GUARDED_BY(cs_store);
     182             : 
     183             : public:
     184             :     GovernanceStore();
     185       10467 :     ~GovernanceStore() = default;
     186             : 
     187             :     template<typename Stream>
     188        3716 :     void Serialize(Stream &s) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store)
     189             :     {
     190        3716 :         LOCK(cs_store);
     191        3716 :         s   << SERIALIZATION_VERSION_STRING
     192        3716 :             << mapErasedGovernanceObjects
     193        3716 :             << cmapInvalidVotes
     194        3716 :             << cmmapOrphanVotes
     195        3716 :             << mapObjects
     196        3716 :             << mapLastMasternodeObject
     197        3716 :             << *lastMNListForVotingKeys;
     198        3716 :     }
     199             : 
     200             :     template<typename Stream>
     201        3410 :     void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs_store)
     202             :     {
     203        3410 :         Clear();
     204             : 
     205        3410 :         LOCK(cs_store);
     206        3410 :         std::string strVersion;
     207        3410 :         s >> strVersion;
     208        3410 :         if (strVersion != SERIALIZATION_VERSION_STRING) {
     209           0 :             return;
     210             :         }
     211             : 
     212        3410 :         s   >> mapErasedGovernanceObjects
     213        3410 :             >> cmapInvalidVotes
     214        3410 :             >> cmmapOrphanVotes
     215        3410 :             >> mapObjects
     216        3410 :             >> mapLastMasternodeObject
     217        3410 :             >> *lastMNListForVotingKeys;
     218        3410 :     }
     219             : 
     220             :     void Clear()
     221             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     222             : 
     223             :     std::string ToString() const
     224             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     225             : };
     226             : 
     227             : //
     228             : // Governance Manager : Contains all proposals for the budget
     229             : //
     230             : class CGovernanceManager : public GovernanceStore
     231             : {
     232             : private:
     233             :     using db_type = CFlatDB<GovernanceStore>;
     234             :     using object_ref_cm_t = CacheMap<uint256, std::shared_ptr<CGovernanceObject>>;
     235             : 
     236             : private:
     237             :     const std::unique_ptr<db_type> m_db;
     238             :     bool is_loaded{false};
     239             : 
     240             :     CMasternodeMetaMan& m_mn_metaman;
     241             :     const ChainstateManager& m_chainman;
     242             :     governance::SuperblockManager& m_superblocks;
     243             :     CDeterministicMNManager& m_dmnman;
     244             :     CMasternodeSync& m_mn_sync;
     245             : 
     246             :     int64_t nTimeLastDiff{0};
     247             :     // keep track of current block height
     248             :     int nCachedBlockHeight{0};
     249             :     object_ref_cm_t cmapVoteToObject;
     250             :     std::map<uint256, std::shared_ptr<CGovernanceObject>> mapPostponedObjects;
     251             :     std::set<uint256> setAdditionalRelayObjects;
     252             :     std::map<uint256, std::chrono::seconds> m_requested_hash_time;
     253             :     bool fRateChecksEnabled{true};
     254             : 
     255             :     mutable Mutex cs_relay;
     256             :     std::vector<CInv> m_relay_invs GUARDED_BY(cs_relay);
     257             : 
     258             : public:
     259             :     CGovernanceManager() = delete;
     260             :     CGovernanceManager(const CGovernanceManager&) = delete;
     261             :     CGovernanceManager& operator=(const CGovernanceManager&) = delete;
     262             :     explicit CGovernanceManager(CMasternodeMetaMan& mn_metaman, const ChainstateManager& chainman,
     263             :                                 governance::SuperblockManager& superblocks, CDeterministicMNManager& dmnman,
     264             :                                 CMasternodeSync& mn_sync);
     265             :     ~CGovernanceManager();
     266             : 
     267             :     // Basic initialization and querying
     268      492274 :     bool IsValid() const { return is_loaded; }
     269             :     bool LoadCache(bool load_cache)
     270             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     271             :     [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
     272             :     std::string ToString() const
     273             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     274             :     [[nodiscard]] UniValue ToJson() const
     275             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     276             :     void Clear()
     277             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     278             : 
     279             :     // CGovernanceObject
     280             :     bool AreRateChecksEnabled() const { return fRateChecksEnabled; }
     281             : 
     282             :     // Getters/Setters
     283        3836 :     int GetCachedBlockHeight() const { return nCachedBlockHeight; }
     284           0 :     int64_t GetLastDiffTime() const { return nTimeLastDiff; }
     285             :     std::vector<CGovernanceVote> GetCurrentVotes(const uint256& nParentHash, const COutPoint& mnCollateralOutpointFilter) const
     286             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     287             :     void GetAllNewerThan(std::vector<CGovernanceObject>& objs, int64_t nMoreThanTime,
     288             :                          bool include_postponed = false) const
     289             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     290        4498 :     void UpdateLastDiffTime(int64_t nTimeIn) { nTimeLastDiff = nTimeIn; }
     291             : 
     292             :     // Networking
     293             :     /**
     294             :      * This is called by AlreadyHave in net_processing.cpp as part of the inventory
     295             :      * retrieval process.  Returns true if we want to retrieve the object, otherwise
     296             :      * false. (Note logic is inverted in AlreadyHave).
     297             :      */
     298             :     bool ConfirmInventoryRequest(const CInv& inv)
     299             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     300             :     bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
     301             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay);
     302             :     void RelayObject(const CGovernanceObject& obj)
     303             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_relay);
     304             :     void RelayVote(const CGovernanceVote& vote)
     305             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_relay);
     306             : 
     307             :     // Notification interface trigger
     308             :     void UpdatedBlockTip(const CBlockIndex* pindex)
     309             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay);
     310             : 
     311             :     // Signer interface
     312             :     bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus = false)
     313             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     314             :     std::shared_ptr<CGovernanceObject> FindGovernanceObjectByDataHash(const uint256& nDataHash)
     315             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     316             :     std::vector<std::shared_ptr<const CGovernanceObject>> GetApprovedProposals(const CDeterministicMNList& tip_mn_list)
     317             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     318             :     void AddGovernanceObject(CGovernanceObject& govobj, const std::string& peer_str)
     319             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay);
     320             : 
     321             :     // Thread-safe accessors
     322             :     bool HaveObjectForHash(const uint256& nHash) const
     323             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     324             :     bool HaveVoteForHash(const uint256& nHash) const
     325             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     326             :     bool SerializeObjectForHash(const uint256& nHash, CDataStream& ss) const
     327             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     328             :     bool SerializeVoteForHash(const uint256& nHash, CDataStream& ss) const
     329             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     330             :     std::shared_ptr<CGovernanceObject> FindGovernanceObject(const uint256& nHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     331             :     int GetVoteCount() const
     332             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     333             :     void AddPostponedObject(const CGovernanceObject& govobj)
     334             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     335             : 
     336             :     std::shared_ptr<const CGovernanceObject> FindConstGovernanceObject(const uint256& nHash) const
     337             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     338             : 
     339             :     // Used by NetGovernance
     340             :     std::vector<CInv> FetchRelayInventory() EXCLUSIVE_LOCKS_REQUIRED(!cs_relay);
     341             :     void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     342             :     /** Get hashes of governance objects for which we have orphan votes. Also cleans up expired orphans. */
     343             :     [[nodiscard]] std::vector<uint256> GetOrphanVoteObjectHashes() EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     344             :     std::pair<std::vector<uint256>, std::vector<uint256>> FetchGovernanceObjectVotes(
     345             :         size_t peers_per_hash_max, int64_t now, std::map<uint256, std::map<CService, int64_t>>& map_asked_recently) const
     346             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     347             :     /** Build bloom filter of existing votes for a governance object (for sync requests) */
     348             :     [[nodiscard]] CBloomFilter GetVoteBloomFilter(const uint256& nHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     349             :     /** Returns inventory items for all syncable (non-deleted, non-expired) governance objects */
     350             :     [[nodiscard]] std::vector<CInv> GetSyncableObjectInvs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     351             :     /** Returns inventory items for syncable votes on a specific object, filtered by bloom filter */
     352             :     [[nodiscard]] std::vector<CInv> GetSyncableVoteInvs(const uint256& nProp, const CBloomFilter& filter) const
     353             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     354             :     /// Called to indicate a requested object or vote has been received
     355             :     bool AcceptMessage(const uint256& nHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     356             :     bool ProcessObject(const std::string& peer_str, const uint256& hash, CGovernanceObject& govobj)
     357             :         EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_store, !cs_relay);
     358             : 
     359             :     CDeterministicMNManager& GetMNManager();
     360             :     /** Process a governance vote. Returns true on success.
     361             :      *  If the vote is for an unknown object (orphan), hashToRequest is set to the object hash. */
     362             :     bool ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception, uint256& hashToRequest)
     363             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     364             : 
     365             : 
     366             : private:
     367             :     // Internal counterparts to "Thread-safe accessors"
     368             :     void AddPostponedObjectInternal(const CGovernanceObject& govobj)
     369             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     370             :     std::shared_ptr<CGovernanceObject> FindGovernanceObjectInternal(const uint256& nHash)
     371             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     372             : 
     373             :     std::shared_ptr<const CGovernanceObject> FindConstGovernanceObjectInternal(const uint256& nHash) const
     374             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     375             : 
     376             :     // Internal counterpart to "Signer interface"
     377             :     void AddGovernanceObjectInternal(CGovernanceObject& govobj, const std::string& peer_str)
     378             :         EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs_store, !cs_relay);
     379             : 
     380             :     // ...
     381             :     void MasternodeRateUpdate(const CGovernanceObject& govobj)
     382             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     383             : 
     384             :     bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus, bool fForce, bool& fRateCheckBypassed)
     385             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     386             : 
     387             :     void CheckPostponedObjects()
     388             :         EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs_store, !cs_relay);
     389             : 
     390             :     void InitOnLoad()
     391             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
     392             : 
     393             :     void CheckOrphanVotes(CGovernanceObject& govobj)
     394             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store, !cs_relay);
     395             : 
     396             :     void RebuildIndexes()
     397             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     398             : 
     399             :     void RemoveInvalidVotes()
     400             :         EXCLUSIVE_LOCKS_REQUIRED(cs_store);
     401             : };
     402             : 
     403             : #endif // BITCOIN_GOVERNANCE_GOVERNANCE_H

Generated by: LCOV version 1.16