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
|