LCOV - code coverage report
Current view: top level - src/evo - dmnstate.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 168 169 99.4 %
Date: 2026-06-25 07:23:43 Functions: 235 268 87.7 %

          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_DMNSTATE_H
       6             : #define BITCOIN_EVO_DMNSTATE_H
       7             : 
       8             : #include <bls/bls.h>
       9             : #include <crypto/sha256.h>
      10             : #include <evo/providertx.h>
      11             : #include <netaddress.h>
      12             : #include <pubkey.h>
      13             : #include <script/script.h>
      14             : #include <util/helpers.h>
      15             : 
      16             : #include <memory>
      17             : #include <utility>
      18             : 
      19             : #include <boost/hana/for_each.hpp>
      20             : #include <boost/hana/tuple.hpp>
      21             : 
      22             : class CDeterministicMNState;
      23             : class CProRegTx;
      24             : struct RPCResult;
      25             : namespace llmq {
      26             : class CFinalCommitment;
      27             : } // namespace llmq
      28             : 
      29             : class UniValue;
      30             : 
      31      988014 : class CDeterministicMNState
      32             : {
      33             : private:
      34      280857 :     int nPoSeBanHeight{-1};
      35             : 
      36             :     friend class CDeterministicMNStateDiff;
      37             :     friend class CDeterministicMNStateDiffLegacy;
      38             : 
      39             : public:
      40      278068 :     int nVersion{ProTxVersion::LegacyBLS};
      41             : 
      42      280857 :     int nRegisteredHeight{-1};
      43      280857 :     int nLastPaidHeight{0};
      44      280857 :     int nConsecutivePayments{0};
      45      280857 :     int nPoSePenalty{0};
      46      280857 :     int nPoSeRevivedHeight{-1};
      47      280857 :     uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED};
      48             : 
      49             :     // the block hash X blocks after registration, used in quorum calculations
      50             :     uint256 confirmedHash;
      51             :     // sha256(proTxHash, confirmedHash) to speed up quorum calculations
      52             :     // please note that this is NOT a double-sha256 hash
      53             :     uint256 confirmedHashWithProRegTxHash;
      54             : 
      55             :     CKeyID keyIDOwner;
      56             :     CBLSLazyPublicKey pubKeyOperator;
      57             :     CKeyID keyIDVoting;
      58      278068 :     std::shared_ptr<NetInfoInterface> netInfo{nullptr};
      59             :     CScript scriptPayout;
      60             :     CScript scriptOperatorPayout;
      61             : 
      62      278068 :     uint160 platformNodeID{};
      63      278068 :     uint16_t platformP2PPort{0};
      64      278068 :     uint16_t platformHTTPPort{0};
      65             : 
      66             : public:
      67     1073212 :     CDeterministicMNState() = default;
      68        8367 :     explicit CDeterministicMNState(const CProRegTx& proTx) :
      69        2789 :         nVersion(proTx.nVersion),
      70        2789 :         keyIDOwner(proTx.keyIDOwner),
      71        2789 :         pubKeyOperator(proTx.pubKeyOperator),
      72        2789 :         keyIDVoting(proTx.keyIDVoting),
      73        2789 :         netInfo(proTx.netInfo),
      74        2789 :         scriptPayout(proTx.scriptPayout),
      75        2789 :         platformNodeID(proTx.platformNodeID),
      76        2789 :         platformP2PPort(proTx.platformP2PPort),
      77        2789 :         platformHTTPPort(proTx.platformHTTPPort)
      78        2789 :     {
      79        5578 :     }
      80             :     template <typename Stream>
      81       39060 :     CDeterministicMNState(deserialize_type, Stream& s) { s >> *this; }
      82             : 
      83       61401 :     SERIALIZE_METHODS(CDeterministicMNState, obj)
      84             :     {
      85       20467 :         READWRITE(
      86             :             obj.nVersion,
      87             :             obj.nRegisteredHeight,
      88             :             obj.nLastPaidHeight,
      89             :             obj.nConsecutivePayments,
      90             :             obj.nPoSePenalty,
      91             :             obj.nPoSeRevivedHeight,
      92             :             obj.nPoSeBanHeight,
      93             :             obj.nRevocationReason,
      94             :             obj.confirmedHash,
      95             :             obj.confirmedHashWithProRegTxHash,
      96             :             obj.keyIDOwner);
      97       20467 :         READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), obj.nVersion == ProTxVersion::LegacyBLS));
      98       20467 :         READWRITE(
      99             :             obj.keyIDVoting,
     100             :             NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(obj.netInfo),
     101             :                               obj.nVersion >= ProTxVersion::ExtAddr),
     102             :             obj.scriptPayout,
     103             :             obj.scriptOperatorPayout,
     104             :             obj.platformNodeID);
     105       20467 :         if (obj.nVersion < ProTxVersion::ExtAddr) {
     106       20367 :             READWRITE(
     107             :             obj.platformP2PPort,
     108             :             obj.platformHTTPPort);
     109       20367 :         }
     110       20467 :     }
     111             : 
     112          79 :     void ResetOperatorFields()
     113             :     {
     114          79 :         nVersion = ProTxVersion::LegacyBLS;
     115          79 :         pubKeyOperator = CBLSLazyPublicKey();
     116          79 :         netInfo = NetInfoInterface::MakeNetInfo(nVersion);
     117          79 :         scriptOperatorPayout = CScript();
     118          79 :         nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
     119          79 :         platformNodeID = uint160();
     120          79 :     }
     121         273 :     void BanIfNotBanned(int height)
     122             :     {
     123         273 :         if (!IsBanned()) {
     124         269 :             nPoSeBanHeight = height;
     125         269 :         }
     126         273 :     }
     127           5 :     int GetBannedHeight() const
     128             :     {
     129           5 :         return nPoSeBanHeight;
     130             :     }
     131     5949036 :     bool IsBanned() const
     132             :     {
     133     5949036 :         return nPoSeBanHeight != -1;
     134             :     }
     135         122 :     void Revive(int nRevivedHeight)
     136             :     {
     137         122 :         nPoSePenalty = 0;
     138         122 :         nPoSeBanHeight = -1;
     139         122 :         nPoSeRevivedHeight = nRevivedHeight;
     140         122 :     }
     141        3427 :     void UpdateConfirmedHash(const uint256& _proTxHash, const uint256& _confirmedHash)
     142             :     {
     143        3427 :         confirmedHash = _confirmedHash;
     144        3427 :         CSHA256 h;
     145        3427 :         h.Write(_proTxHash.begin(), _proTxHash.size());
     146        3427 :         h.Write(_confirmedHash.begin(), _confirmedHash.size());
     147        3427 :         h.Finalize(confirmedHashWithProRegTxHash.begin());
     148        3427 :     }
     149             : 
     150             : public:
     151             :     std::string ToString() const;
     152             :     [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
     153             :     [[nodiscard]] UniValue ToJson(MnType nType) const;
     154             : };
     155             : 
     156             : class CDeterministicMNStateDiff
     157             : {
     158             : public:
     159             :     enum Field : uint32_t {
     160             :         Field_nVersion = 0x0001,
     161             :         Field_nRegisteredHeight = 0x0002,
     162             :         Field_nLastPaidHeight = 0x0004,
     163             :         Field_nPoSePenalty = 0x0008,
     164             :         Field_nPoSeRevivedHeight = 0x0010,
     165             :         Field_nPoSeBanHeight = 0x0020,
     166             :         Field_nRevocationReason = 0x0040,
     167             :         Field_confirmedHash = 0x0080,
     168             :         Field_confirmedHashWithProRegTxHash = 0x0100,
     169             :         Field_keyIDOwner = 0x0200,
     170             :         Field_pubKeyOperator = 0x0400,
     171             :         Field_keyIDVoting = 0x0800,
     172             :         Field_netInfo = 0x1000,
     173             :         Field_scriptPayout = 0x2000,
     174             :         Field_scriptOperatorPayout = 0x4000,
     175             :         Field_nConsecutivePayments = 0x8000,
     176             :         Field_platformNodeID = 0x10000,
     177             :         Field_platformP2PPort = 0x20000,
     178             :         Field_platformHTTPPort = 0x40000,
     179             :     };
     180             : 
     181             : private:
     182             :     template <auto CDeterministicMNState::*Field, uint32_t Mask>
     183             :     struct Member
     184             :     {
     185             :         static constexpr uint32_t mask = Mask;
     186      767014 :         static auto& get(CDeterministicMNState& state) { return state.*Field; }
     187     9020254 :         static const auto& get(const CDeterministicMNState& state) { return state.*Field; }
     188             :     };
     189             : 
     190             : #define DMN_STATE_MEMBER(name) Member<&CDeterministicMNState::name, Field_##name>{}
     191             :     static constexpr auto members = boost::hana::make_tuple(
     192             :         DMN_STATE_MEMBER(nVersion),
     193             :         DMN_STATE_MEMBER(nRegisteredHeight),
     194             :         DMN_STATE_MEMBER(nLastPaidHeight),
     195             :         DMN_STATE_MEMBER(nPoSePenalty),
     196             :         DMN_STATE_MEMBER(nPoSeRevivedHeight),
     197             :         DMN_STATE_MEMBER(nPoSeBanHeight),
     198             :         DMN_STATE_MEMBER(nRevocationReason),
     199             :         DMN_STATE_MEMBER(confirmedHash),
     200             :         DMN_STATE_MEMBER(confirmedHashWithProRegTxHash),
     201             :         DMN_STATE_MEMBER(keyIDOwner),
     202             :         DMN_STATE_MEMBER(pubKeyOperator),
     203             :         DMN_STATE_MEMBER(keyIDVoting),
     204             :         DMN_STATE_MEMBER(netInfo),
     205             :         DMN_STATE_MEMBER(scriptPayout),
     206             :         DMN_STATE_MEMBER(scriptOperatorPayout),
     207             :         DMN_STATE_MEMBER(nConsecutivePayments),
     208             :         DMN_STATE_MEMBER(platformNodeID),
     209             :         DMN_STATE_MEMBER(platformP2PPort),
     210             :         DMN_STATE_MEMBER(platformHTTPPort)
     211             :     );
     212             : #undef DMN_STATE_MEMBER
     213             : 
     214             : public:
     215      268279 :     uint32_t fields{0};
     216             :     // we reuse the state class, but only the members as noted by fields are valid
     217             :     CDeterministicMNState state;
     218             : 
     219             : public:
     220          63 :     CDeterministicMNStateDiff() = default;
     221      629121 :     CDeterministicMNStateDiff(const CDeterministicMNState& a, const CDeterministicMNState& b)
     222      209707 :     {
     223     4194140 :         boost::hana::for_each(members, [&](auto&& member) {
     224             :             using BaseType = std::decay_t<decltype(member)>;
     225             :             if constexpr (BaseType::mask == Field_netInfo) {
     226      209707 :                 if (util::shared_ptr_not_equal(member.get(a), member.get(b))) {
     227        2610 :                     member.get(state) = member.get(b);
     228        2610 :                     fields |= member.mask;
     229        2610 :                 }
     230             :             } else {
     231     3774726 :                 if (member.get(a) != member.get(b)) {
     232      231284 :                     member.get(state) = member.get(b);
     233      231284 :                     fields |= member.mask;
     234      231284 :                 }
     235             :             }
     236     3984433 :         });
     237      209707 :         if ((fields & Field_pubKeyOperator) || (fields & Field_netInfo)) {
     238             :             // pubKeyOperator and netInfo need nVersion
     239        2612 :             state.nVersion = b.nVersion;
     240        2612 :             fields |= Field_nVersion;
     241        2612 :         }
     242      419414 :     }
     243             :     template <typename Stream>
     244      175653 :     CDeterministicMNStateDiff(deserialize_type, Stream& s) { s >> *this; }
     245             : 
     246             :     [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
     247             :     [[nodiscard]] UniValue ToJson(MnType nType) const;
     248             : 
     249     1118205 :     SERIALIZE_METHODS(CDeterministicMNStateDiff, obj)
     250             :     {
     251      372735 :         READWRITE(VARINT(obj.fields));
     252             : 
     253      372735 :         if (((obj.fields & Field_pubKeyOperator) || (obj.fields & Field_netInfo)) && !(obj.fields & Field_nVersion)) {
     254             :             // pubKeyOperator and netInfo need nVersion
     255           0 :             throw std::ios_base::failure("Invalid data, nVersion unset when pubKeyOperator or netInfo set");
     256             :         }
     257             : 
     258     7454700 :         boost::hana::for_each(members, [&](auto&& member) {
     259             :             using BaseType = std::decay_t<decltype(member)>;
     260             :             if constexpr (BaseType::mask == Field_pubKeyOperator) {
     261      372735 :                 if (obj.fields & member.mask) {
     262         208 :                     READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator), obj.state.nVersion == ProTxVersion::LegacyBLS));
     263         208 :                 }
     264             :             } else if constexpr (BaseType::mask == Field_netInfo) {
     265      372735 :                 if (obj.fields & member.mask) {
     266        4647 :                     READWRITE(NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(obj.state.netInfo),
     267             :                                                 obj.state.nVersion >= ProTxVersion::ExtAddr));
     268        4647 :                 }
     269             :             } else {
     270     6336495 :                 if (obj.fields & member.mask) {
     271      420120 :                     READWRITE(member.get(obj.state));
     272      420120 :                 }
     273             :             }
     274     7081965 :         });
     275      372735 :     }
     276             : 
     277      401501 :     void ApplyToState(CDeterministicMNState& target) const
     278             :     {
     279     8030020 :         boost::hana::for_each(members, [&](auto&& member) {
     280     7628519 :             if (fields & member.mask) {
     281      465247 :                 member.get(target) = member.get(state);
     282      465247 :             }
     283     7628519 :         });
     284      401501 :     }
     285             : };
     286             : 
     287             : // Legacy deserializer class
     288             : class CDeterministicMNStateDiffLegacy
     289             : {
     290             : private:
     291             :     // Legacy field enum with original bit positions
     292             :     enum LegacyField : uint32_t {
     293             :         LegacyField_nRegisteredHeight = 0x0001,
     294             :         LegacyField_nLastPaidHeight = 0x0002,
     295             :         LegacyField_nPoSePenalty = 0x0004,
     296             :         LegacyField_nPoSeRevivedHeight = 0x0008,
     297             :         LegacyField_nPoSeBanHeight = 0x0010,
     298             :         LegacyField_nRevocationReason = 0x0020,
     299             :         LegacyField_confirmedHash = 0x0040,
     300             :         LegacyField_confirmedHashWithProRegTxHash = 0x0080,
     301             :         LegacyField_keyIDOwner = 0x0100,
     302             :         LegacyField_pubKeyOperator = 0x0200,
     303             :         LegacyField_keyIDVoting = 0x0400,
     304             :         LegacyField_netInfo = 0x0800,
     305             :         LegacyField_scriptPayout = 0x1000,
     306             :         LegacyField_scriptOperatorPayout = 0x2000,
     307             :         LegacyField_nConsecutivePayments = 0x4000,
     308             :         LegacyField_platformNodeID = 0x8000,
     309             :         LegacyField_platformP2PPort = 0x10000,
     310             :         LegacyField_platformHTTPPort = 0x20000,
     311             :         LegacyField_nVersion = 0x40000,
     312             :     };
     313             : 
     314             :     // Legacy member template with old bit positions
     315             :     template <auto CDeterministicMNState::*Field, uint32_t Mask>
     316             :     struct LegacyMember {
     317             :         static constexpr uint32_t mask = Mask;
     318           2 :         static auto& get(CDeterministicMNState& state) { return state.*Field; }
     319           2 :         static const auto& get(const CDeterministicMNState& state) { return state.*Field; }
     320             :     };
     321             : 
     322             : #define LEGACY_DMN_STATE_MEMBER(name) \
     323             :     LegacyMember<&CDeterministicMNState::name, LegacyField_##name> {}
     324             :     static constexpr auto legacy_members = boost::hana::make_tuple(
     325             :         LEGACY_DMN_STATE_MEMBER(nRegisteredHeight),
     326             :         LEGACY_DMN_STATE_MEMBER(nLastPaidHeight),
     327             :         LEGACY_DMN_STATE_MEMBER(nPoSePenalty),
     328             :         LEGACY_DMN_STATE_MEMBER(nPoSeRevivedHeight),
     329             :         LEGACY_DMN_STATE_MEMBER(nPoSeBanHeight),
     330             :         LEGACY_DMN_STATE_MEMBER(nRevocationReason),
     331             :         LEGACY_DMN_STATE_MEMBER(confirmedHash),
     332             :         LEGACY_DMN_STATE_MEMBER(confirmedHashWithProRegTxHash),
     333             :         LEGACY_DMN_STATE_MEMBER(keyIDOwner),
     334             :         LEGACY_DMN_STATE_MEMBER(pubKeyOperator),
     335             :         LEGACY_DMN_STATE_MEMBER(keyIDVoting),
     336             :         LEGACY_DMN_STATE_MEMBER(netInfo),
     337             :         LEGACY_DMN_STATE_MEMBER(scriptPayout),
     338             :         LEGACY_DMN_STATE_MEMBER(scriptOperatorPayout),
     339             :         LEGACY_DMN_STATE_MEMBER(nConsecutivePayments),
     340             :         LEGACY_DMN_STATE_MEMBER(platformNodeID),
     341             :         LEGACY_DMN_STATE_MEMBER(platformP2PPort),
     342             :         LEGACY_DMN_STATE_MEMBER(platformHTTPPort),
     343             :         LEGACY_DMN_STATE_MEMBER(nVersion)
     344             :     );
     345             : #undef LEGACY_DMN_STATE_MEMBER
     346             : 
     347             : public:
     348          22 :     uint32_t fields{0};
     349             :     CDeterministicMNState state;
     350             : 
     351          63 :     CDeterministicMNStateDiffLegacy() = default;
     352             : 
     353             :     template <typename Stream>
     354           3 :     CDeterministicMNStateDiffLegacy(deserialize_type, Stream& s)
     355           1 :     {
     356           1 :         s >> *this;
     357           2 :     }
     358             : 
     359             :     // Used for testing only
     360             :     template<typename Stream>
     361           1 :     void Serialize(Stream& s) const
     362             :     {
     363           1 :         s << VARINT(fields);
     364             : 
     365          20 :         boost::hana::for_each(legacy_members, [&](auto&& member) {
     366             :             using BaseType = std::decay_t<decltype(member)>;
     367             :             if constexpr (BaseType::mask == LegacyField_pubKeyOperator) {
     368           1 :                 if (fields & member.mask) {
     369             :                     // We serialize it as is, no version wrapper is used here
     370           1 :                     s << state.pubKeyOperator;
     371           1 :                 }
     372             :             } else if constexpr (BaseType::mask == LegacyField_netInfo) {
     373           1 :                 if (fields & member.mask) {
     374             :                     // Legacy format supports non-extended addresses only
     375           1 :                     s << NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(state.netInfo),
     376             :                                                 /*is_extended=*/false);
     377           1 :                 }
     378             :             } else {
     379          17 :                 if (fields & member.mask) {
     380           2 :                     s << member.get(state);
     381           2 :                 }
     382             :             }
     383          19 :         });
     384           1 :     }
     385             : 
     386             :     // Deserialize using legacy format
     387             :     template<typename Stream>
     388           1 :     void Unserialize(Stream& s)
     389             :     {
     390           1 :         s >> VARINT(fields);
     391             : 
     392          20 :         boost::hana::for_each(legacy_members, [&](auto&& member) {
     393             :             using BaseType = std::decay_t<decltype(member)>;
     394             :             if constexpr (BaseType::mask == LegacyField_pubKeyOperator) {
     395           1 :                 if (fields & member.mask) {
     396             :                     // We'll set proper scheme later in MigrateLegacyDiffs()
     397           1 :                     s >> CBLSLazyPublicKeyVersionWrapper(state.pubKeyOperator, /*legacy=*/true);
     398           1 :                 }
     399             :             } else if constexpr (BaseType::mask == LegacyField_netInfo) {
     400           1 :                 if (fields & member.mask) {
     401             :                     // Legacy format supports non-extended addresses only
     402           1 :                     s >> NetInfoSerWrapper(state.netInfo, /*is_extended=*/false);
     403           1 :                 }
     404             :             } else {
     405          17 :                 if (fields & member.mask) {
     406           2 :                     s >> member.get(state);
     407           2 :                 }
     408             :             }
     409          19 :         });
     410           1 :     }
     411             : 
     412             :     // Convert to new format
     413          21 :     CDeterministicMNStateDiff ToNewFormat() const
     414             :     {
     415          21 :         CDeterministicMNStateDiff newDiff;
     416          21 :         newDiff.state = state;
     417             : 
     418             :         // Convert field bits to new positions
     419             : #define LEGACY_DMN_STATE_BITS(name) \
     420             :     if (fields & LegacyField_##name) newDiff.fields |= CDeterministicMNStateDiff::Field_##name;
     421          21 :         LEGACY_DMN_STATE_BITS(nVersion)
     422          21 :         LEGACY_DMN_STATE_BITS(nRegisteredHeight)
     423          21 :         LEGACY_DMN_STATE_BITS(nLastPaidHeight)
     424          21 :         LEGACY_DMN_STATE_BITS(nPoSePenalty)
     425          21 :         LEGACY_DMN_STATE_BITS(nPoSeRevivedHeight)
     426          21 :         LEGACY_DMN_STATE_BITS(nPoSeBanHeight)
     427          21 :         LEGACY_DMN_STATE_BITS(nRevocationReason)
     428          21 :         LEGACY_DMN_STATE_BITS(confirmedHash)
     429          21 :         LEGACY_DMN_STATE_BITS(confirmedHashWithProRegTxHash)
     430          21 :         LEGACY_DMN_STATE_BITS(keyIDOwner)
     431          21 :         LEGACY_DMN_STATE_BITS(pubKeyOperator)
     432          21 :         LEGACY_DMN_STATE_BITS(keyIDVoting)
     433          21 :         LEGACY_DMN_STATE_BITS(netInfo)
     434          21 :         LEGACY_DMN_STATE_BITS(scriptPayout)
     435          21 :         LEGACY_DMN_STATE_BITS(scriptOperatorPayout)
     436          21 :         LEGACY_DMN_STATE_BITS(nConsecutivePayments)
     437          21 :         LEGACY_DMN_STATE_BITS(platformNodeID)
     438          21 :         LEGACY_DMN_STATE_BITS(platformP2PPort)
     439          21 :         LEGACY_DMN_STATE_BITS(platformHTTPPort)
     440             : #undef LEGACY_DMN_STATE_BITS
     441             : 
     442          21 :         return newDiff;
     443          21 :     }
     444             : };
     445             : 
     446             : #endif // BITCOIN_EVO_DMNSTATE_H

Generated by: LCOV version 1.16