LCOV - code coverage report
Current view: top level - src/evo - dmnstate.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 166 169 98.2 %
Date: 2026-06-25 07:23:51 Functions: 169 268 63.1 %

          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       16501 : class CDeterministicMNState
      32             : {
      33             : private:
      34        2157 :     int nPoSeBanHeight{-1};
      35             : 
      36             :     friend class CDeterministicMNStateDiff;
      37             :     friend class CDeterministicMNStateDiffLegacy;
      38             : 
      39             : public:
      40        2088 :     int nVersion{ProTxVersion::LegacyBLS};
      41             : 
      42        2157 :     int nRegisteredHeight{-1};
      43        2157 :     int nLastPaidHeight{0};
      44        2157 :     int nConsecutivePayments{0};
      45        2157 :     int nPoSePenalty{0};
      46        2157 :     int nPoSeRevivedHeight{-1};
      47        2157 :     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        2088 :     std::shared_ptr<NetInfoInterface> netInfo{nullptr};
      59             :     CScript scriptPayout;
      60             :     CScript scriptOperatorPayout;
      61             : 
      62        2088 :     uint160 platformNodeID{};
      63        2088 :     uint16_t platformP2PPort{0};
      64        2088 :     uint16_t platformHTTPPort{0};
      65             : 
      66             : public:
      67        8352 :     CDeterministicMNState() = default;
      68         207 :     explicit CDeterministicMNState(const CProRegTx& proTx) :
      69          69 :         nVersion(proTx.nVersion),
      70          69 :         keyIDOwner(proTx.keyIDOwner),
      71          69 :         pubKeyOperator(proTx.pubKeyOperator),
      72          69 :         keyIDVoting(proTx.keyIDVoting),
      73          69 :         netInfo(proTx.netInfo),
      74          69 :         scriptPayout(proTx.scriptPayout),
      75          69 :         platformNodeID(proTx.platformNodeID),
      76          69 :         platformP2PPort(proTx.platformP2PPort),
      77          69 :         platformHTTPPort(proTx.platformHTTPPort)
      78          69 :     {
      79         138 :     }
      80             :     template <typename Stream>
      81           0 :     CDeterministicMNState(deserialize_type, Stream& s) { s >> *this; }
      82             : 
      83         231 :     SERIALIZE_METHODS(CDeterministicMNState, obj)
      84             :     {
      85          77 :         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          77 :         READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), obj.nVersion == ProTxVersion::LegacyBLS));
      98          77 :         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          77 :         if (obj.nVersion < ProTxVersion::ExtAddr) {
     106          77 :             READWRITE(
     107             :             obj.platformP2PPort,
     108             :             obj.platformHTTPPort);
     109          77 :         }
     110          77 :     }
     111             : 
     112          13 :     void ResetOperatorFields()
     113             :     {
     114          13 :         nVersion = ProTxVersion::LegacyBLS;
     115          13 :         pubKeyOperator = CBLSLazyPublicKey();
     116          13 :         netInfo = NetInfoInterface::MakeNetInfo(nVersion);
     117          13 :         scriptOperatorPayout = CScript();
     118          13 :         nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
     119          13 :         platformNodeID = uint160();
     120          13 :     }
     121          14 :     void BanIfNotBanned(int height)
     122             :     {
     123          14 :         if (!IsBanned()) {
     124          10 :             nPoSeBanHeight = height;
     125          10 :         }
     126          14 :     }
     127           5 :     int GetBannedHeight() const
     128             :     {
     129           5 :         return nPoSeBanHeight;
     130             :     }
     131      103311 :     bool IsBanned() const
     132             :     {
     133      103311 :         return nPoSeBanHeight != -1;
     134             :     }
     135           4 :     void Revive(int nRevivedHeight)
     136             :     {
     137           4 :         nPoSePenalty = 0;
     138           4 :         nPoSeBanHeight = -1;
     139           4 :         nPoSeRevivedHeight = nRevivedHeight;
     140           4 :     }
     141         130 :     void UpdateConfirmedHash(const uint256& _proTxHash, const uint256& _confirmedHash)
     142             :     {
     143         130 :         confirmedHash = _confirmedHash;
     144         130 :         CSHA256 h;
     145         130 :         h.Write(_proTxHash.begin(), _proTxHash.size());
     146         130 :         h.Write(_confirmedHash.begin(), _confirmedHash.size());
     147         130 :         h.Finalize(confirmedHashWithProRegTxHash.begin());
     148         130 :     }
     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        2116 :         static auto& get(CDeterministicMNState& state) { return state.*Field; }
     187       83920 :         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        2064 :     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        6129 :     CDeterministicMNStateDiff(const CDeterministicMNState& a, const CDeterministicMNState& b)
     222        2043 :     {
     223       40860 :         boost::hana::for_each(members, [&](auto&& member) {
     224             :             using BaseType = std::decay_t<decltype(member)>;
     225             :             if constexpr (BaseType::mask == Field_netInfo) {
     226        2043 :                 if (util::shared_ptr_not_equal(member.get(a), member.get(b))) {
     227           8 :                     member.get(state) = member.get(b);
     228           8 :                     fields |= member.mask;
     229           8 :                 }
     230             :             } else {
     231       36774 :                 if (member.get(a) != member.get(b)) {
     232        2088 :                     member.get(state) = member.get(b);
     233        2088 :                     fields |= member.mask;
     234        2088 :                 }
     235             :             }
     236       38817 :         });
     237        2043 :         if ((fields & Field_pubKeyOperator) || (fields & Field_netInfo)) {
     238             :             // pubKeyOperator and netInfo need nVersion
     239          10 :             state.nVersion = b.nVersion;
     240          10 :             fields |= Field_nVersion;
     241          10 :         }
     242        4086 :     }
     243             :     template <typename Stream>
     244           0 :     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       12252 :     SERIALIZE_METHODS(CDeterministicMNStateDiff, obj)
     250             :     {
     251        4084 :         READWRITE(VARINT(obj.fields));
     252             : 
     253        4084 :         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       81680 :         boost::hana::for_each(members, [&](auto&& member) {
     259             :             using BaseType = std::decay_t<decltype(member)>;
     260             :             if constexpr (BaseType::mask == Field_pubKeyOperator) {
     261        4084 :                 if (obj.fields & member.mask) {
     262          10 :                     READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator), obj.state.nVersion == ProTxVersion::LegacyBLS));
     263          10 :                 }
     264             :             } else if constexpr (BaseType::mask == Field_netInfo) {
     265        4084 :                 if (obj.fields & member.mask) {
     266          14 :                     READWRITE(NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(obj.state.netInfo),
     267             :                                                 obj.state.nVersion >= ProTxVersion::ExtAddr));
     268          14 :                 }
     269             :             } else {
     270       69428 :                 if (obj.fields & member.mask) {
     271        4170 :                     READWRITE(member.get(obj.state));
     272        4170 :                 }
     273             :             }
     274       77596 :         });
     275        4084 :     }
     276             : 
     277           4 :     void ApplyToState(CDeterministicMNState& target) const
     278             :     {
     279          80 :         boost::hana::for_each(members, [&](auto&& member) {
     280          76 :             if (fields & member.mask) {
     281          20 :                 member.get(target) = member.get(state);
     282          20 :             }
     283          76 :         });
     284           4 :     }
     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