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
|