LCOV - code coverage report
Current view: top level - src/evo - deterministicmns.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 256 279 91.8 %
Date: 2026-06-25 07:23:43 Functions: 105 110 95.5 %

          Line data    Source code
       1             : // Copyright (c) 2018-2025 The Dash Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #ifndef BITCOIN_EVO_DETERMINISTICMNS_H
       6             : #define BITCOIN_EVO_DETERMINISTICMNS_H
       7             : 
       8             : #include <evo/dmn_types.h>
       9             : #include <evo/dmnstate.h>
      10             : #include <evo/providertx.h>
      11             : #include <evo/types.h>
      12             : 
      13             : #include <arith_uint256.h>
      14             : #include <clientversion.h>
      15             : #include <consensus/params.h>
      16             : #include <crypto/common.h>
      17             : #include <saltedhasher.h>
      18             : #include <scheduler.h>
      19             : #include <sync.h>
      20             : 
      21             : #include <gsl/pointers.h>
      22             : #include <immer/map.hpp>
      23             : 
      24             : #include <atomic>
      25             : #include <limits>
      26             : #include <numeric>
      27             : #include <unordered_map>
      28             : #include <utility>
      29             : 
      30             : class CBlock;
      31             : class CBlockIndex;
      32             : class CCoinsViewCache;
      33             : class ChainstateManager;
      34             : class CEvoDB;
      35             : class CSimplifiedMNList;
      36             : class CSimplifiedMNListEntry;
      37             : class CMasternodeMetaMan;
      38             : class CSpecialTxProcessor;
      39             : struct RPCResult;
      40             : 
      41             : extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration)
      42             : 
      43             : class CDeterministicMN
      44             : {
      45             : private:
      46        9765 :     uint64_t internalId{std::numeric_limits<uint64_t>::max()};
      47             : 
      48             : public:
      49             :     static constexpr uint16_t MN_VERSION_FORMAT = 2;
      50             :     static constexpr uint16_t MN_CURRENT_FORMAT = MN_VERSION_FORMAT;
      51             : 
      52             :     uint256 proTxHash;
      53             :     COutPoint collateralOutpoint;
      54       12556 :     uint16_t nOperatorReward{0};
      55        9765 :     MnType nType{MnType::Regular};
      56             :     std::shared_ptr<const CDeterministicMNState> pdmnState;
      57             : 
      58             :     CDeterministicMN() = delete; // no default constructor, must specify internalId
      59       11164 :     explicit CDeterministicMN(uint64_t _internalId, MnType mnType = MnType::Regular) :
      60        2791 :         internalId(_internalId),
      61        2791 :         nType(mnType)
      62        2791 :     {
      63             :         // only non-initial values
      64        2791 :         assert(_internalId != std::numeric_limits<uint64_t>::max());
      65        5582 :     }
      66             :     template <typename Stream>
      67       39060 :     CDeterministicMN(deserialize_type, Stream& s) { s >> *this; }
      68             : 
      69       61365 :     SERIALIZE_METHODS(CDeterministicMN, obj)
      70             :     {
      71       20455 :         READWRITE(obj.proTxHash);
      72       20455 :         READWRITE(VARINT(obj.internalId));
      73       20455 :         READWRITE(obj.collateralOutpoint);
      74       20455 :         READWRITE(obj.nOperatorReward);
      75       20455 :         READWRITE(obj.pdmnState);
      76             :         // We can't know if we are serialising for the Disk or for the Network here (s.GetType() is not accessible)
      77             :         // Therefore if s.GetVersion() == CLIENT_VERSION -> Then we know we are serialising for the Disk
      78             :         // Otherwise, we can safely check with protocol versioning logic so we won't break old clients
      79       20455 :         if (s.GetVersion() == CLIENT_VERSION || s.GetVersion() >= DMN_TYPE_PROTO_VERSION) {
      80       20455 :             READWRITE(obj.nType);
      81       20455 :         } else {
      82           0 :             SER_READ(obj, obj.nType = MnType::Regular);
      83             :         }
      84       20455 :     }
      85             : 
      86             :     [[nodiscard]] uint64_t GetInternalId() const;
      87             : 
      88             :     [[nodiscard]] CSimplifiedMNListEntry to_sml_entry() const;
      89             :     [[nodiscard]] std::string ToString() const;
      90             : 
      91             :     [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
      92             :     [[nodiscard]] UniValue ToJson() const;
      93             : };
      94             : 
      95             : class CDeterministicMNListDiff;
      96             : 
      97             : template <typename Stream, typename K, typename T, typename Hash, typename Equal>
      98             : void SerializeImmerMap(Stream& os, const immer::map<K, T, Hash, Equal>& m)
      99             : {
     100             :     WriteCompactSize(os, m.size());
     101             :     for (typename immer::map<K, T, Hash, Equal>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
     102             :         Serialize(os, (*mi));
     103             : }
     104             : 
     105             : template <typename Stream, typename K, typename T, typename Hash, typename Equal>
     106             : void UnserializeImmerMap(Stream& is, immer::map<K, T, Hash, Equal>& m)
     107             : {
     108             :     m = immer::map<K, T, Hash, Equal>();
     109             :     unsigned int nSize = ReadCompactSize(is);
     110             :     for (unsigned int i = 0; i < nSize; i++) {
     111             :         std::pair<K, T> item;
     112             :         Unserialize(is, item);
     113             :         m = m.set(item.first, item.second);
     114             :     }
     115             : }
     116             : 
     117             : // For some reason the compiler is not able to choose the correct Serialize/Deserialize methods without a specialized
     118             : // version of SerReadWrite. It otherwise always chooses the version that calls a.Serialize()
     119             : template<typename Stream, typename K, typename T, typename Hash, typename Equal>
     120             : inline void SerReadWrite(Stream& s, const immer::map<K, T, Hash, Equal>& m, CSerActionSerialize ser_action)
     121             : {
     122             :     ::SerializeImmerMap(s, m);
     123             : }
     124             : 
     125             : template<typename Stream, typename K, typename T, typename Hash, typename Equal>
     126             : inline void SerReadWrite(Stream& s, immer::map<K, T, Hash, Equal>& obj, CSerActionUnserialize ser_action)
     127             : {
     128             :     ::UnserializeImmerMap(s, obj);
     129             : }
     130             : 
     131             : 
     132             : class CDeterministicMNList
     133             : {
     134             : private:
     135             :     struct ImmerHasher
     136             :     {
     137     5199196 :         size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
     138             :     };
     139             : 
     140             : public:
     141             :     using MnMap = immer::map<uint256, CDeterministicMNCPtr, ImmerHasher>;
     142             :     using MnInternalIdMap = immer::map<uint64_t, uint256>;
     143             :     using MnUniquePropertyMap = immer::map<uint256, std::pair<uint256, uint32_t>, ImmerHasher>;
     144             : 
     145      578503 :     struct Counts {
     146      578503 :         size_t m_total_evo{0};
     147      578503 :         size_t m_total_mn{0};
     148      578503 :         size_t m_total_weighted{0};
     149      578503 :         size_t m_valid_evo{0};
     150      578503 :         size_t m_valid_mn{0};
     151      578503 :         size_t m_valid_weighted{0};
     152             : 
     153      507620 :         [[nodiscard]] size_t total() const { return m_total_mn + m_total_evo; }
     154       30185 :         [[nodiscard]] size_t enabled() const { return m_valid_mn + m_valid_evo; }
     155             :     };
     156             : 
     157             : private:
     158             :     uint256 blockHash;
     159     3451602 :     int nHeight{-1};
     160     3451602 :     uint32_t nTotalRegisteredCount{0};
     161             :     MnMap mnMap;
     162             :     MnInternalIdMap mnInternalIdMap;
     163             : 
     164             :     // map of unique properties like address and keys
     165             :     // we keep track of this as checking for duplicates would otherwise be painfully slow
     166             :     MnUniquePropertyMap mnUniquePropertyMap;
     167             : 
     168             :     // This SML could be null
     169             :     // This cache is used to improve performance and meant to be reused
     170             :     // for multiple CDeterministicMNList until mnMap is actually changed.
     171             :     // Calls of AddMN, RemoveMN and (in some cases) UpdateMN reset this cache;
     172             :     // it happens also for indirect calls such as ApplyDiff
     173             :     // Thread safety: Protected by its own mutex for thread-safe access
     174             :     mutable Mutex m_cached_sml_mutex;
     175             :     mutable std::shared_ptr<const CSimplifiedMNList> m_cached_sml GUARDED_BY(m_cached_sml_mutex);
     176             : 
     177             :     // Private helper method to invalidate SML cache
     178       80298 :     void InvalidateSMLCache() EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex)
     179             :     {
     180       80298 :         LOCK(m_cached_sml_mutex);
     181       80298 :         m_cached_sml = nullptr;
     182       80298 :     }
     183             : 
     184             : public:
     185    10354807 :     CDeterministicMNList() = default;
     186        2589 :     explicit CDeterministicMNList(const uint256& _blockHash, int _height, uint32_t _totalRegisteredCount) :
     187         863 :         blockHash(_blockHash),
     188         863 :         nHeight(_height),
     189         863 :         nTotalRegisteredCount(_totalRegisteredCount)
     190         863 :     {
     191         863 :         assert(nHeight >= 0);
     192        1726 :     }
     193             : 
     194             :     // Copy constructor
     195     3279996 :     CDeterministicMNList(const CDeterministicMNList& other) :
     196     1093332 :         blockHash(other.blockHash),
     197     1093332 :         nHeight(other.nHeight),
     198     1093332 :         nTotalRegisteredCount(other.nTotalRegisteredCount),
     199     1093332 :         mnMap(other.mnMap),
     200     1093332 :         mnInternalIdMap(other.mnInternalIdMap),
     201     1093332 :         mnUniquePropertyMap(other.mnUniquePropertyMap)
     202     1093332 :     {
     203     1093332 :         LOCK(other.m_cached_sml_mutex);
     204     1093332 :         m_cached_sml = other.m_cached_sml;
     205     2186664 :     }
     206             : 
     207             :     // Assignment operator
     208     2485061 :     CDeterministicMNList& operator=(const CDeterministicMNList& other)
     209             :         EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex, !other.m_cached_sml_mutex)
     210             :     {
     211     2485061 :         if (this != &other) {
     212     2485061 :             blockHash = other.blockHash;
     213     2485061 :             nHeight = other.nHeight;
     214     2485061 :             nTotalRegisteredCount = other.nTotalRegisteredCount;
     215     2485061 :             mnMap = other.mnMap;
     216     2485061 :             mnInternalIdMap = other.mnInternalIdMap;
     217     2485061 :             mnUniquePropertyMap = other.mnUniquePropertyMap;
     218             : 
     219     2485061 :             LOCK2(m_cached_sml_mutex, other.m_cached_sml_mutex);
     220     2485061 :             m_cached_sml = other.m_cached_sml;
     221     2485061 :         }
     222     2485061 :         return *this;
     223           0 :     }
     224             : 
     225             :     template <typename Stream, typename Operation>
     226        7337 :     inline void SerializationOpBase(Stream& s, Operation ser_action)
     227             :     {
     228        7337 :         READWRITE(blockHash);
     229        7337 :         READWRITE(nHeight);
     230        7337 :         READWRITE(nTotalRegisteredCount);
     231        7337 :     }
     232             : 
     233             :     template<typename Stream>
     234        3884 :     void Serialize(Stream& s) const
     235             :     {
     236        3884 :         const_cast<CDeterministicMNList*>(this)->SerializationOpBase(s, CSerActionSerialize());
     237             : 
     238             :         // Serialize the map as a vector
     239        3884 :         WriteCompactSize(s, mnMap.size());
     240        8979 :         for (const auto& [_, dmn] : mnMap) {
     241       10190 :             s << *dmn;
     242             :         }
     243        3884 :     }
     244             : 
     245             :     template <typename Stream>
     246        3453 :     void Unserialize(Stream& s) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex)
     247             :     {
     248        3453 :         Clear();
     249             : 
     250        3453 :         SerializationOpBase(s, CSerActionUnserialize());
     251             : 
     252        6511 :         for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) {
     253        3058 :             AddMN(std::make_shared<CDeterministicMN>(deserialize, s), /*fBumpTotalCount=*/false);
     254        3058 :         }
     255        3453 :     }
     256             : 
     257        3453 :     void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex)
     258             :     {
     259        3453 :         blockHash = uint256{};
     260        3453 :         nHeight = -1;
     261        3453 :         nTotalRegisteredCount = 0;
     262        3453 :         mnMap = MnMap();
     263        3453 :         mnUniquePropertyMap = MnUniquePropertyMap();
     264        3453 :         mnInternalIdMap = MnInternalIdMap();
     265        3453 :         InvalidateSMLCache();
     266        3453 :     }
     267             : 
     268      578504 :     [[nodiscard]] Counts GetCounts() const
     269             :     {
     270      578504 :         Counts ret;
     271     2745924 :         for (const auto& [_, dmn] : mnMap) {
     272     2167420 :             const bool is_evo = dmn->nType == MnType::Evo;
     273     2167420 :             const bool is_valid = !dmn->pdmnState->IsBanned();
     274     2167420 :             const auto weight = GetMnType(dmn->nType).voting_weight;
     275     2167420 :             if (is_evo) {
     276      181495 :                 ret.m_total_evo++;
     277      181495 :                 if (is_valid) {
     278      154043 :                     ret.m_valid_evo++;
     279      154043 :                 }
     280      181495 :             } else {
     281     1985925 :                 ret.m_total_mn++;
     282     1985925 :                 if (is_valid) {
     283     1949507 :                     ret.m_valid_mn++;
     284     1949507 :                 }
     285             :             }
     286     2167420 :             if (is_valid) {
     287     2103550 :                 ret.m_valid_weighted += weight;
     288     2103550 :             }
     289     2167420 :             ret.m_total_weighted += weight;
     290             :         }
     291      578504 :         return ret;
     292             :     }
     293             : 
     294             :     /**
     295             :      * Execute a callback on all masternodes in the mnList. This will pass a reference
     296             :      * of each masternode to the callback function. This should be preferred over ForEachMNShared.
     297             :      * @param onlyValid Run on all masternodes, or only "valid" (not banned) masternodes
     298             :      * @param cb callback to execute
     299             :      */
     300      443927 :     void ForEachMN(bool onlyValid, std::function<void(const CDeterministicMN&)> cb) const
     301             :     {
     302     2092508 :         for (const auto& p : mnMap) {
     303     1648581 :             if (!onlyValid || !p.second->pdmnState->IsBanned()) {
     304     1648579 :                 cb(*p.second);
     305     1648579 :             }
     306             :         }
     307      443927 :     }
     308             : 
     309             :     /**
     310             :      * Prefer ForEachMN. Execute a callback on all masternodes in the mnList.
     311             :      * This will pass a non-null shared_ptr of each masternode to the callback function.
     312             :      * Use this function only when a shared_ptr is needed in order to take shared ownership.
     313             :      * @param onlyValid Run on all masternodes, or only "valid" (not banned) masternodes
     314             :      * @param cb callback to execute
     315             :      */
     316      606041 :     void ForEachMNShared(bool onlyValid, std::function<void(const CDeterministicMNCPtr&)> cb) const
     317             :     {
     318     3579774 :         for (const auto& p : mnMap) {
     319     2973733 :             if (!onlyValid || !p.second->pdmnState->IsBanned()) {
     320     2890766 :                 cb(p.second);
     321     2890766 :             }
     322             :         }
     323      606041 :     }
     324             : 
     325     2444805 :     [[nodiscard]] const uint256& GetBlockHash() const
     326             :     {
     327     2444805 :         return blockHash;
     328             :     }
     329      381970 :     void SetBlockHash(const uint256& _blockHash)
     330             :     {
     331      381970 :         blockHash = _blockHash;
     332      381970 :     }
     333     4055821 :     [[nodiscard]] int GetHeight() const
     334             :     {
     335     4055821 :         assert(nHeight >= 0);
     336     4055821 :         return nHeight;
     337             :     }
     338      214461 :     void SetHeight(int _height)
     339             :     {
     340      214461 :         assert(_height >= 0);
     341      214461 :         nHeight = _height;
     342      214461 :     }
     343        2789 :     [[nodiscard]] uint32_t GetTotalRegisteredCount() const
     344             :     {
     345        2789 :         return nTotalRegisteredCount;
     346             :     }
     347             : 
     348             :     [[nodiscard]] bool IsMNValid(const uint256& proTxHash) const;
     349             :     [[nodiscard]] bool IsMNPoSeBanned(const uint256& proTxHash) const;
     350             : 
     351      110561 :     [[nodiscard]] bool HasMN(const uint256& proTxHash) const
     352             :     {
     353      110561 :         return GetMN(proTxHash) != nullptr;
     354             :     }
     355      229812 :     [[nodiscard]] bool HasMNByCollateral(const COutPoint& collateralOutpoint) const
     356             :     {
     357      229812 :         return GetMNByCollateral(collateralOutpoint) != nullptr;
     358             :     }
     359           0 :     [[nodiscard]] bool HasValidMNByCollateral(const COutPoint& collateralOutpoint) const
     360             :     {
     361           0 :         return GetValidMNByCollateral(collateralOutpoint) != nullptr;
     362             :     }
     363             :     [[nodiscard]] CDeterministicMNCPtr GetMN(const uint256& proTxHash) const;
     364             :     [[nodiscard]] CDeterministicMNCPtr GetValidMN(const uint256& proTxHash) const;
     365             :     [[nodiscard]] CDeterministicMNCPtr GetMNByOperatorKey(const CBLSPublicKey& pubKey) const;
     366             :     [[nodiscard]] CDeterministicMNCPtr GetMNByCollateral(const COutPoint& collateralOutpoint) const;
     367             :     [[nodiscard]] CDeterministicMNCPtr GetValidMNByCollateral(const COutPoint& collateralOutpoint) const;
     368             :     [[nodiscard]] CDeterministicMNCPtr GetMNByService(const CService& service) const;
     369             :     [[nodiscard]] CDeterministicMNCPtr GetMNByInternalId(uint64_t internalId) const;
     370             :     [[nodiscard]] CDeterministicMNCPtr GetMNPayee(gsl::not_null<const CBlockIndex*> pindexPrev) const;
     371             : 
     372             :     /**
     373             :      * Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct
     374             :      * as PoSe banning might occur later
     375             :      * @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetCounts twice.
     376             :      */
     377             :     [[nodiscard]] std::vector<CDeterministicMNCPtr> GetProjectedMNPayees(gsl::not_null<const CBlockIndex* const> pindexPrev, int nCount = std::numeric_limits<int>::max()) const;
     378             : 
     379             :     /**
     380             :      * Calculates CSimplifiedMNList for current list and cache it
     381             :      * Thread safety: Uses internal mutex for thread-safe cache access
     382             :      */
     383             :     gsl::not_null<std::shared_ptr<const CSimplifiedMNList>> to_sml() const EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     384             : 
     385             :     /**
     386             :      * Calculates the maximum penalty which is allowed at the height of this MN list. It is dynamic and might change
     387             :      * for every block.
     388             :      */
     389             :     [[nodiscard]] int CalcMaxPoSePenalty() const;
     390             : 
     391             :     /**
     392             :      * Returns a the given percentage from the max penalty for this MN list. Always use this method to calculate the
     393             :      * value later passed to PoSePunish. The percentage should be high enough to take per-block penalty decreasing for MNs
     394             :      * into account. This means, if you want to accept 2 failures per payment cycle, you should choose a percentage that
     395             :      * is higher then 50%, e.g. 66%.
     396             :      */
     397             :     [[nodiscard]] int CalcPenalty(int percent) const;
     398             : 
     399             :     /**
     400             :      * Punishes a MN for misbehavior. If the resulting penalty score of the MN reaches the max penalty, it is banned.
     401             :      * Penalty scores are only increased when the MN is not already banned, which means that after banning the penalty
     402             :      * might appear lower then the current max penalty, while the MN is still banned.
     403             :      */
     404             :     void PoSePunish(const uint256& proTxHash, int penalty, bool debugLogs) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     405             : 
     406             :     void DecreaseScores() EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     407             :     /**
     408             :      * Decrease penalty score of MN by 1.
     409             :      * Only allowed on non-banned MNs.
     410             :      */
     411             :     void PoSeDecrease(const CDeterministicMN& dmn) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     412             : 
     413             :     [[nodiscard]] CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const;
     414             :     /**
     415             :      * Apply Diff modifies current object.
     416             :      * It is more efficient than creating a copy due to heavy copy constructor.
     417             :      * Calculating for old block may require up to {DISK_SNAPSHOT_PERIOD} object copy & destroy.
     418             :      */
     419             :     void ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, const CDeterministicMNListDiff& diff)
     420             :         EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     421             : 
     422             :     void AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount = true) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     423             :     void UpdateMN(const CDeterministicMN& oldDmn, const std::shared_ptr<const CDeterministicMNState>& pdmnState)
     424             :         EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     425             :     void UpdateMN(const uint256& proTxHash, const std::shared_ptr<const CDeterministicMNState>& pdmnState)
     426             :         EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     427             :     void UpdateMN(const CDeterministicMN& oldDmn, const CDeterministicMNStateDiff& stateDiff)
     428             :         EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     429             :     void RemoveMN(const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
     430             : 
     431             :     template <typename T>
     432       27930 :     [[nodiscard]] bool HasUniqueProperty(const T& v) const
     433             :     {
     434       27930 :         return mnUniquePropertyMap.count(GetUniquePropertyHash(v)) != 0;
     435             :     }
     436             :     template <typename T>
     437      855957 :     [[nodiscard]] CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const
     438             :     {
     439      855957 :         auto p = mnUniquePropertyMap.find(GetUniquePropertyHash(v));
     440      855957 :         if (!p) {
     441      659633 :             return nullptr;
     442             :         }
     443      196324 :         return GetMN(p->first);
     444      855957 :     }
     445             : 
     446             :     // Compare two masternode lists for equality, ignoring non-deterministic members.
     447             :     // Non-deterministic members (nTotalRegisteredCount, internalId) can differ between
     448             :     // nodes due to different sync histories, but don't affect consensus validity.
     449          11 :     bool IsEqual(const CDeterministicMNList& rhs) const
     450             :     {
     451             :         // Compare deterministic metadata
     452          22 :         if (blockHash != rhs.blockHash ||
     453          11 :             nHeight != rhs.nHeight ||
     454          11 :             mnUniquePropertyMap != rhs.mnUniquePropertyMap) {
     455           0 :             return false;
     456             :         }
     457             : 
     458             :         // Compare map sizes (actual entries compared below)
     459             :         // Note: Not comparing nTotalRegisteredCount (non-deterministic)
     460          11 :         if (mnMap.size() != rhs.mnMap.size() ||
     461          11 :             mnInternalIdMap.size() != rhs.mnInternalIdMap.size()) {
     462           0 :             return false;
     463             :         }
     464             : 
     465             :         // Compare each masternode entry
     466          17 :         for (const auto& [proTxHash, dmn] : mnMap) {
     467          12 :             auto dmn_rhs = rhs.mnMap.find(proTxHash);
     468           6 :             if (dmn_rhs == nullptr) {
     469           0 :                 return false;
     470             :             }
     471             : 
     472             :             // Compare deterministic masternode fields
     473             :             // Note: Not comparing internalId (non-deterministic)
     474          12 :             if (dmn->proTxHash != dmn_rhs->get()->proTxHash ||
     475           6 :                 dmn->collateralOutpoint != dmn_rhs->get()->collateralOutpoint ||
     476           6 :                 dmn->nOperatorReward != dmn_rhs->get()->nOperatorReward ||
     477           6 :                 dmn->nType != dmn_rhs->get()->nType ||
     478             :                 // Use SerializeHash for pdmnState to avoid enumerating all state fields
     479           6 :                 SerializeHash(*dmn->pdmnState) != SerializeHash(*dmn_rhs->get()->pdmnState)) {
     480           0 :                 return false;
     481             :             }
     482             :         }
     483          11 :         return true;
     484          11 :     }
     485             : 
     486             : private:
     487             :     template <typename T>
     488     1207021 :     [[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const
     489             :     {
     490             : #define DMNL_NO_TEMPLATE(name) \
     491             :     static_assert(!std::is_same_v<std::decay_t<T>, name>, "GetUniquePropertyHash cannot be templated against " #name)
     492             :         DMNL_NO_TEMPLATE(CBLSPublicKey);
     493             :         DMNL_NO_TEMPLATE(ExtNetInfo);
     494             :         DMNL_NO_TEMPLATE(MnNetInfo);
     495             :         DMNL_NO_TEMPLATE(NetInfoEntry);
     496             :         DMNL_NO_TEMPLATE(NetInfoInterface);
     497             :         DMNL_NO_TEMPLATE(std::shared_ptr<NetInfoInterface>);
     498             : #undef DMNL_NO_TEMPLATE
     499     1207021 :         int ser_version{PROTOCOL_VERSION};
     500             :         if constexpr (std::is_same_v<std::decay_t<T>, CService>) {
     501             :             // Special handling is required if we're using addresses that can only be (de)serialized using
     502             :             // ADDRv2. Without this step, the address gets truncated, the hashmap gets contaminated with
     503             :             // an invalid entry and subsequent attempts at registering ADDRv2 entries get blocked. We cannot
     504             :             // apply this treatment ADDRv1 compatible addresses for backwards compatibility with the existing map.
     505      101637 :             if (!v.IsAddrV1Compatible()) {
     506         174 :                 ser_version |= ADDRV2_FORMAT;
     507         174 :             }
     508             :         }
     509     1207021 :         return ::SerializeHash(v, /*nType=*/SER_GETHASH, /*nVersion=*/ser_version);
     510             :     }
     511             :     template <typename T>
     512      313275 :     [[nodiscard]] bool AddUniqueProperty(const CDeterministicMN& dmn, const T& v)
     513             :     {
     514      305751 :         static const T nullValue{};
     515      313275 :         if (v == nullValue) {
     516           0 :             return false;
     517             :         }
     518             : 
     519      313275 :         auto hash = GetUniquePropertyHash(v);
     520      313275 :         auto oldEntry = mnUniquePropertyMap.find(hash);
     521      313275 :         if (oldEntry != nullptr && oldEntry->first != dmn.proTxHash) {
     522           0 :             return false;
     523             :         }
     524      313275 :         std::pair<uint256, uint32_t> newEntry(dmn.proTxHash, 1);
     525      313275 :         if (oldEntry != nullptr) {
     526           0 :             newEntry.second = oldEntry->second + 1;
     527           0 :         }
     528      313275 :         mnUniquePropertyMap = mnUniquePropertyMap.set(hash, newEntry);
     529      313275 :         return true;
     530      313275 :     }
     531             :     template <typename T>
     532        9860 :     [[nodiscard]] bool DeleteUniqueProperty(const CDeterministicMN& dmn, const T& oldValue)
     533             :     {
     534        9021 :         static const T nullValue{};
     535        9860 :         if (oldValue == nullValue) {
     536           0 :             return false;
     537             :         }
     538             : 
     539        9860 :         auto oldHash = GetUniquePropertyHash(oldValue);
     540        9860 :         auto p = mnUniquePropertyMap.find(oldHash);
     541        9860 :         if (p == nullptr || p->first != dmn.proTxHash) {
     542           0 :             return false;
     543             :         }
     544        9860 :         if (p->second == 1) {
     545        9860 :             mnUniquePropertyMap = mnUniquePropertyMap.erase(oldHash);
     546        9860 :         } else {
     547           0 :             mnUniquePropertyMap = mnUniquePropertyMap.set(oldHash, std::make_pair(dmn.proTxHash, p->second - 1));
     548             :         }
     549        9860 :         return true;
     550        9860 :     }
     551             :     template <typename T>
     552     1178664 :     [[nodiscard]] bool UpdateUniqueProperty(const CDeterministicMN& dmn, const T& oldValue, const T& newValue)
     553             :     {
     554     1178664 :         if (oldValue == newValue) {
     555     1177666 :             return true;
     556             :         }
     557         259 :         static const T nullValue{};
     558             : 
     559         998 :         if (oldValue != nullValue && !DeleteUniqueProperty(dmn, oldValue)) {
     560           0 :             return false;
     561             :         }
     562             : 
     563         998 :         if (newValue != nullValue && !AddUniqueProperty(dmn, newValue)) {
     564           0 :             return false;
     565             :         }
     566         998 :         return true;
     567     1178664 :     }
     568             : 
     569      214465 :     friend bool operator==(const CDeterministicMNList& a, const CDeterministicMNList& b)
     570             :     {
     571      214948 :         return  a.blockHash == b.blockHash &&
     572         483 :                 a.nHeight == b.nHeight &&
     573         483 :                 a.nTotalRegisteredCount == b.nTotalRegisteredCount &&
     574         483 :                 a.mnMap == b.mnMap &&
     575         483 :                 a.mnInternalIdMap == b.mnInternalIdMap &&
     576         483 :                 a.mnUniquePropertyMap == b.mnUniquePropertyMap;
     577             :     }
     578             : };
     579             : 
     580     1011368 : class CDeterministicMNListDiff
     581             : {
     582             : public:
     583      517027 :     int nHeight{-1}; //memory only
     584             : 
     585             :     std::vector<CDeterministicMNCPtr> addedMNs;
     586             :     // keys are all relating to the internalId of MNs
     587             :     std::unordered_map<uint64_t, CDeterministicMNStateDiff> updatedMNs;
     588             :     std::set<uint64_t> removedMns;
     589             : 
     590             :     template<typename Stream>
     591      378393 :     void Serialize(Stream& s) const
     592             :     {
     593      378393 :         s << addedMNs;
     594             : 
     595      378393 :         WriteCompactSize(s, updatedMNs.size());
     596      692577 :         for (const auto& [internalId, pdmnState] : updatedMNs) {
     597      628368 :             WriteVarInt<Stream, VarIntMode::DEFAULT, uint64_t>(s, internalId);
     598      628368 :             s << pdmnState;
     599             :         }
     600             : 
     601      378393 :         WriteCompactSize(s, removedMns.size());
     602      379742 :         for (const auto& internalId : removedMns) {
     603        1349 :             WriteVarInt<Stream, VarIntMode::DEFAULT, uint64_t>(s, internalId);
     604             :         }
     605      378393 :     }
     606             : 
     607             :     template <typename Stream>
     608      135175 :     void Unserialize(Stream& s)
     609             :     {
     610      135175 :         UnserializeImpl(s, false); // Default: new format
     611      135175 :     }
     612             : 
     613             :     // Legacy-aware unserialize method
     614             :     template <typename Stream>
     615           0 :     void UnserializeLegacyFormat(Stream& s)
     616             :     {
     617           0 :         UnserializeImpl(s, true); // Legacy format
     618           0 :     }
     619             : 
     620             : private:
     621             :     template <typename Stream>
     622      135175 :     void UnserializeImpl(Stream& s, bool isLegacyFormat)
     623             :     {
     624             :         // Reset all collections before reading
     625      135175 :         addedMNs.clear();
     626      135175 :         updatedMNs.clear();
     627      135175 :         removedMns.clear();
     628             : 
     629      141882 :         for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) {
     630        6707 :             addedMNs.push_back(std::make_shared<CDeterministicMN>(deserialize, s));
     631        6707 :         }
     632             : 
     633      193726 :         for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) {
     634       58551 :             uint64_t internalId = ReadVarInt<Stream, VarIntMode::DEFAULT, uint64_t>(s);
     635             :             // CDeterministicMNState can have newer fields but doesn't need migration logic here as
     636             :             // CDeterministicMNStateDiff is always serialised using a bitmask and new fields have a new bit guide value,
     637             :             // so we are good to continue. We do need migration logic to change field order though.
     638       58551 :             if (isLegacyFormat) {
     639             :                 // Use legacy deserializer for old format
     640           0 :                 CDeterministicMNStateDiffLegacy legacyDiff(deserialize, s);
     641             :                 // Convert to new format and store
     642           0 :                 updatedMNs.emplace(internalId, legacyDiff.ToNewFormat());
     643           0 :             } else {
     644             :                 // Use current deserializer for new format
     645       58551 :                 updatedMNs.emplace(internalId, CDeterministicMNStateDiff(deserialize, s));
     646             :             }
     647       58551 :         }
     648             : 
     649      135361 :         for (size_t to_read = ReadCompactSize(s); to_read > 0; --to_read) {
     650         186 :             uint64_t internalId = ReadVarInt<Stream, VarIntMode::DEFAULT, uint64_t>(s);
     651         186 :             removedMns.emplace(internalId);
     652         186 :         }
     653      135175 :     }
     654             : 
     655             : public:
     656      182770 :     bool HasChanges() const
     657             :     {
     658      182770 :         return !addedMNs.empty() || !updatedMNs.empty() || !removedMns.empty();
     659             :     }
     660             : };
     661             : 
     662             : 
     663             : constexpr int llmq_max_blocks() {
     664             :     int max_blocks{0};
     665             :     for (const auto& llmq : Consensus::available_llmqs) {
     666             :         int blocks = (llmq.useRotation ? 1 : llmq.signingActiveQuorumCount) * llmq.dkgInterval;
     667             :         max_blocks = std::max(max_blocks, blocks);
     668             :     }
     669             :     return max_blocks;
     670             : }
     671             : 
     672      105821 : struct MNListUpdates
     673             : {
     674             :     CDeterministicMNList old_list;
     675             :     CDeterministicMNList new_list;
     676             :     CDeterministicMNListDiff diff;
     677             : };
     678             : 
     679             : class CDeterministicMNManager
     680             : {
     681             :     static constexpr int DISK_SNAPSHOT_PERIOD = 576; // once per day
     682             :     // keep cache for enough disk snapshots to have all active quourms covered
     683             :     static constexpr int DISK_SNAPSHOTS = llmq_max_blocks() / DISK_SNAPSHOT_PERIOD + 1;
     684             :     static constexpr int LIST_DIFFS_CACHE_SIZE = DISK_SNAPSHOT_PERIOD * DISK_SNAPSHOTS;
     685             : 
     686             : private:
     687             :     Mutex cs;
     688             :     Mutex cs_cleanup;
     689             :     // We have performed CleanupCache() on this height.
     690             :     int did_cleanup GUARDED_BY(cs_cleanup) {0};
     691             : 
     692             :     // Main thread has indicated we should perform cleanup up to this height
     693             :     std::atomic<int> to_cleanup {0};
     694             : 
     695             :     CEvoDB& m_evoDb;
     696             :     CMasternodeMetaMan& m_mn_metaman;
     697             : 
     698             :     Uint256HashMap<CDeterministicMNList> mnListsCache GUARDED_BY(cs);
     699             :     Uint256HashMap<CDeterministicMNListDiff> mnListDiffsCache GUARDED_BY(cs);
     700             :     const CBlockIndex* tipIndex GUARDED_BY(cs) {nullptr};
     701             :     const CBlockIndex* m_initial_snapshot_index GUARDED_BY(cs) {nullptr};
     702             : 
     703             : public:
     704             :     CDeterministicMNManager() = delete;
     705             :     CDeterministicMNManager(const CDeterministicMNManager&) = delete;
     706             :     CDeterministicMNManager& operator=(const CDeterministicMNManager&) = delete;
     707             :     explicit CDeterministicMNManager(CEvoDB& evoDb, CMasternodeMetaMan& mn_metaman);
     708             :     ~CDeterministicMNManager();
     709             : 
     710             :     bool ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex, BlockValidationState& state,
     711             :                       const CDeterministicMNList& newList, std::optional<MNListUpdates>& updatesRet)
     712             :         EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
     713             :     bool UndoBlock(gsl::not_null<const CBlockIndex*> pindex, std::optional<MNListUpdates>& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     714             : 
     715             :     void UpdatedBlockTip(gsl::not_null<const CBlockIndex*> pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     716             : 
     717      763002 :     CDeterministicMNList GetListForBlock(gsl::not_null<const CBlockIndex*> pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs) {
     718      763002 :         LOCK(cs);
     719      763002 :         return GetListForBlockInternal(pindex);
     720      763006 :     };
     721             :     CDeterministicMNList GetListAtChainTip() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     722             : 
     723             :     // Test if given TX is a ProRegTx which also contains the collateral at index n
     724             :     static bool IsProTxWithCollateral(const CTransactionRef& tx, uint32_t n);
     725             : 
     726             :     void DoMaintenance() EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cleanup);
     727             : 
     728             :     // Recalculate and optionally repair diffs between snapshots
     729         415 :     struct RecalcDiffsResult {
     730         415 :         int start_height{0};
     731         415 :         int stop_height{0};
     732         415 :         int diffs_recalculated{0};
     733         415 :         int snapshots_verified{0};
     734             :         std::vector<std::string> verification_errors;
     735             :         std::vector<std::string> repair_errors;
     736             :     };
     737             : 
     738             :     // Callback type for building a new MN list from a block
     739             :     using BuildListFromBlockFunc = std::function<bool(
     740             :         const CBlock& block,
     741             :         gsl::not_null<const CBlockIndex*> pindexPrev,
     742             :         const CDeterministicMNList& prevList,
     743             :         const CCoinsViewCache& view,
     744             :         bool debugLogs,
     745             :         BlockValidationState& state,
     746             :         CDeterministicMNList& mnListRet)>;
     747             : 
     748             :     [[nodiscard]] RecalcDiffsResult RecalculateAndRepairDiffs(const CBlockIndex* start_index,
     749             :                                                               const CBlockIndex* stop_index, ChainstateManager& chainman,
     750             :                                                               BuildListFromBlockFunc build_list_func, bool repair)
     751             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     752             :     [[nodiscard]] bool IsRepaired() const;
     753             :     void CompleteRepair();
     754             : 
     755             :     // Migration support for nVersion-first CDeterministicMNStateDiff format
     756             :     [[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
     757             :     [[nodiscard]] bool MigrateLegacyDiffs(const CBlockIndex* const tip_index) EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
     758             : 
     759             : private:
     760             :     void CleanupCache(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
     761             :     CDeterministicMNList GetListForBlockInternal(gsl::not_null<const CBlockIndex*> pindex) EXCLUSIVE_LOCKS_REQUIRED(cs);
     762             : 
     763             :     // Helper methods for RecalculateAndRepairDiffs
     764             :     std::vector<const CBlockIndex*> CollectSnapshotBlocks(const CBlockIndex* start_index, const CBlockIndex* stop_index,
     765             :                                                           const Consensus::Params& consensus_params);
     766             :     bool VerifySnapshotPair(const CBlockIndex* from_index, const CBlockIndex* to_index,
     767             :                             const CDeterministicMNList& from_snapshot, const CDeterministicMNList& to_snapshot,
     768             :                             RecalcDiffsResult& result);
     769             :     std::vector<std::pair<uint256, CDeterministicMNListDiff>> RepairSnapshotPair(
     770             :         const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot,
     771             :         const CDeterministicMNList& to_snapshot, BuildListFromBlockFunc build_list_func, RecalcDiffsResult& result);
     772             :     void WriteRepairedDiffs(const std::vector<std::pair<uint256, CDeterministicMNListDiff>>& recalculated_diffs,
     773             :                             RecalcDiffsResult& result) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     774             : };
     775             : #endif // BITCOIN_EVO_DETERMINISTICMNS_H

Generated by: LCOV version 1.16