Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #ifndef BITCOIN_GOVERNANCE_GOVERNANCE_H
6 : #define BITCOIN_GOVERNANCE_GOVERNANCE_H
7 :
8 : #include <cachemap.h>
9 : #include <cachemultimap.h>
10 : #include <primitives/transaction.h>
11 : #include <sync.h>
12 :
13 : #include <chrono>
14 : #include <limits>
15 : #include <map>
16 : #include <memory>
17 : #include <set>
18 : #include <string>
19 : #include <vector>
20 :
21 : class CBloomFilter;
22 : class CBlockIndex;
23 : class CConnman;
24 : class CDataStream;
25 : class CDeterministicMNList;
26 : class CDeterministicMNManager;
27 : class ChainstateManager;
28 : template<typename T>
29 : class CFlatDB;
30 : class CGovernanceException;
31 : class CGovernanceObject;
32 : class CGovernanceVote;
33 : class CInv;
34 : class CMasternodeMetaMan;
35 : class CMasternodeSync;
36 : class CService;
37 : struct RPCResult;
38 : class UniValue;
39 :
40 : namespace governance {
41 : class SuperblockManager;
42 : } // namespace governance
43 :
44 : using vote_time_pair_t = std::pair<CGovernanceVote, int64_t>;
45 :
46 : static constexpr int RATE_BUFFER_SIZE = 5;
47 : static constexpr bool DEFAULT_GOVERNANCE_ENABLE{true};
48 :
49 : extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration)
50 :
51 : class CRateCheckBuffer
52 : {
53 : private:
54 : std::vector<int64_t> vecTimestamps;
55 :
56 2 : int nDataStart{0};
57 2 : int nDataEnd{0};
58 2 : bool fBufferEmpty{true};
59 :
60 : public:
61 4 : CRateCheckBuffer() :
62 2 : vecTimestamps(RATE_BUFFER_SIZE)
63 2 : {
64 4 : }
65 :
66 16 : void AddTimestamp(int64_t nTimestamp)
67 : {
68 16 : if ((nDataEnd == nDataStart) && !fBufferEmpty) {
69 : // Buffer full, discard 1st element
70 6 : nDataStart = (nDataStart + 1) % RATE_BUFFER_SIZE;
71 6 : }
72 16 : vecTimestamps[nDataEnd] = nTimestamp;
73 16 : nDataEnd = (nDataEnd + 1) % RATE_BUFFER_SIZE;
74 16 : fBufferEmpty = false;
75 16 : }
76 :
77 21 : int64_t GetMinTimestamp() const
78 : {
79 21 : int nIndex = nDataStart;
80 21 : int64_t nMin = std::numeric_limits<int64_t>::max();
81 21 : if (fBufferEmpty) {
82 1 : return nMin;
83 : }
84 20 : do {
85 74 : if (vecTimestamps[nIndex] < nMin) {
86 20 : nMin = vecTimestamps[nIndex];
87 20 : }
88 74 : nIndex = (nIndex + 1) % RATE_BUFFER_SIZE;
89 74 : } while (nIndex != nDataEnd);
90 20 : return nMin;
91 21 : }
92 :
93 20 : int64_t GetMaxTimestamp() const
94 : {
95 20 : int nIndex = nDataStart;
96 20 : int64_t nMax = 0;
97 20 : if (fBufferEmpty) {
98 1 : return nMax;
99 : }
100 19 : do {
101 73 : if (vecTimestamps[nIndex] > nMax) {
102 73 : nMax = vecTimestamps[nIndex];
103 73 : }
104 73 : nIndex = (nIndex + 1) % RATE_BUFFER_SIZE;
105 73 : } while (nIndex != nDataEnd);
106 19 : return nMax;
107 20 : }
108 :
109 26 : int GetCount() const
110 : {
111 26 : if (fBufferEmpty) {
112 2 : return 0;
113 : }
114 24 : if (nDataEnd > nDataStart) {
115 14 : return nDataEnd - nDataStart;
116 : }
117 10 : return RATE_BUFFER_SIZE - nDataStart + nDataEnd;
118 26 : }
119 :
120 8 : double GetRate() const
121 : {
122 8 : int nCount = GetCount();
123 8 : if (nCount < RATE_BUFFER_SIZE) {
124 6 : return 0.0;
125 : }
126 2 : int64_t nMin = GetMinTimestamp();
127 2 : int64_t nMax = GetMaxTimestamp();
128 2 : if (nMin == nMax) {
129 : // multiple objects with the same timestamp => infinite rate
130 0 : return 1.0e10;
131 : }
132 2 : return double(nCount) / double(nMax - nMin);
133 8 : }
134 :
135 0 : SERIALIZE_METHODS(CRateCheckBuffer, obj)
136 : {
137 0 : READWRITE(obj.vecTimestamps, obj.nDataStart, obj.nDataEnd, obj.fBufferEmpty);
138 0 : }
139 : };
140 :
141 : class GovernanceStore
142 : {
143 : protected:
144 : struct last_object_rec {
145 0 : explicit last_object_rec(bool fStatusOKIn = true) :
146 0 : triggerBuffer(),
147 0 : fStatusOK(fStatusOKIn)
148 0 : {
149 0 : }
150 :
151 0 : SERIALIZE_METHODS(last_object_rec, obj)
152 : {
153 0 : READWRITE(obj.triggerBuffer, obj.fStatusOK);
154 0 : }
155 :
156 : CRateCheckBuffer triggerBuffer;
157 : bool fStatusOK;
158 : };
159 :
160 : using txout_m_t = std::map<COutPoint, last_object_rec>;
161 : using vote_cmm_t = CacheMultiMap<uint256, vote_time_pair_t>;
162 :
163 : protected:
164 : static constexpr int MAX_CACHE_SIZE = 1000000;
165 : static const std::string SERIALIZATION_VERSION_STRING;
166 :
167 : protected:
168 : // critical section to protect the inner data structures
169 : mutable Mutex cs_store;
170 :
171 : // keep track of the scanning errors
172 : std::map<uint256, std::shared_ptr<CGovernanceObject>> mapObjects GUARDED_BY(cs_store);
173 : // mapErasedGovernanceObjects contains key-value pairs, where
174 : // key - governance object's hash
175 : // value - expiration time for deleted objects
176 : std::map<uint256, int64_t> mapErasedGovernanceObjects GUARDED_BY(cs_store);
177 : CacheMap<uint256, CGovernanceVote> cmapInvalidVotes GUARDED_BY(cs_store);
178 : vote_cmm_t cmmapOrphanVotes GUARDED_BY(cs_store);
179 : txout_m_t mapLastMasternodeObject GUARDED_BY(cs_store);
180 : // used to check for changed voting keys
181 : std::shared_ptr<CDeterministicMNList> lastMNListForVotingKeys GUARDED_BY(cs_store);
182 :
183 : public:
184 : GovernanceStore();
185 178 : ~GovernanceStore() = default;
186 :
187 : template<typename Stream>
188 0 : void Serialize(Stream &s) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store)
189 : {
190 0 : LOCK(cs_store);
191 0 : s << SERIALIZATION_VERSION_STRING
192 0 : << mapErasedGovernanceObjects
193 0 : << cmapInvalidVotes
194 0 : << cmmapOrphanVotes
195 0 : << mapObjects
196 0 : << mapLastMasternodeObject
197 0 : << *lastMNListForVotingKeys;
198 0 : }
199 :
200 : template<typename Stream>
201 0 : void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs_store)
202 : {
203 0 : Clear();
204 :
205 0 : LOCK(cs_store);
206 0 : std::string strVersion;
207 0 : s >> strVersion;
208 0 : if (strVersion != SERIALIZATION_VERSION_STRING) {
209 0 : return;
210 : }
211 :
212 0 : s >> mapErasedGovernanceObjects
213 0 : >> cmapInvalidVotes
214 0 : >> cmmapOrphanVotes
215 0 : >> mapObjects
216 0 : >> mapLastMasternodeObject
217 0 : >> *lastMNListForVotingKeys;
218 0 : }
219 :
220 : void Clear()
221 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
222 :
223 : std::string ToString() const
224 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
225 : };
226 :
227 : //
228 : // Governance Manager : Contains all proposals for the budget
229 : //
230 : class CGovernanceManager : public GovernanceStore
231 : {
232 : private:
233 : using db_type = CFlatDB<GovernanceStore>;
234 : using object_ref_cm_t = CacheMap<uint256, std::shared_ptr<CGovernanceObject>>;
235 :
236 : private:
237 : const std::unique_ptr<db_type> m_db;
238 : bool is_loaded{false};
239 :
240 : CMasternodeMetaMan& m_mn_metaman;
241 : const ChainstateManager& m_chainman;
242 : governance::SuperblockManager& m_superblocks;
243 : CDeterministicMNManager& m_dmnman;
244 : CMasternodeSync& m_mn_sync;
245 :
246 : int64_t nTimeLastDiff{0};
247 : // keep track of current block height
248 : int nCachedBlockHeight{0};
249 : object_ref_cm_t cmapVoteToObject;
250 : std::map<uint256, std::shared_ptr<CGovernanceObject>> mapPostponedObjects;
251 : std::set<uint256> setAdditionalRelayObjects;
252 : std::map<uint256, std::chrono::seconds> m_requested_hash_time;
253 : bool fRateChecksEnabled{true};
254 :
255 : mutable Mutex cs_relay;
256 : std::vector<CInv> m_relay_invs GUARDED_BY(cs_relay);
257 :
258 : public:
259 : CGovernanceManager() = delete;
260 : CGovernanceManager(const CGovernanceManager&) = delete;
261 : CGovernanceManager& operator=(const CGovernanceManager&) = delete;
262 : explicit CGovernanceManager(CMasternodeMetaMan& mn_metaman, const ChainstateManager& chainman,
263 : governance::SuperblockManager& superblocks, CDeterministicMNManager& dmnman,
264 : CMasternodeSync& mn_sync);
265 : ~CGovernanceManager();
266 :
267 : // Basic initialization and querying
268 0 : bool IsValid() const { return is_loaded; }
269 : bool LoadCache(bool load_cache)
270 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
271 : [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
272 : std::string ToString() const
273 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
274 : [[nodiscard]] UniValue ToJson() const
275 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
276 : void Clear()
277 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
278 :
279 : // CGovernanceObject
280 : bool AreRateChecksEnabled() const { return fRateChecksEnabled; }
281 :
282 : // Getters/Setters
283 0 : int GetCachedBlockHeight() const { return nCachedBlockHeight; }
284 0 : int64_t GetLastDiffTime() const { return nTimeLastDiff; }
285 : std::vector<CGovernanceVote> GetCurrentVotes(const uint256& nParentHash, const COutPoint& mnCollateralOutpointFilter) const
286 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
287 : void GetAllNewerThan(std::vector<CGovernanceObject>& objs, int64_t nMoreThanTime,
288 : bool include_postponed = false) const
289 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
290 0 : void UpdateLastDiffTime(int64_t nTimeIn) { nTimeLastDiff = nTimeIn; }
291 :
292 : // Networking
293 : /**
294 : * This is called by AlreadyHave in net_processing.cpp as part of the inventory
295 : * retrieval process. Returns true if we want to retrieve the object, otherwise
296 : * false. (Note logic is inverted in AlreadyHave).
297 : */
298 : bool ConfirmInventoryRequest(const CInv& inv)
299 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
300 : bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
301 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay);
302 : void RelayObject(const CGovernanceObject& obj)
303 : EXCLUSIVE_LOCKS_REQUIRED(!cs_relay);
304 : void RelayVote(const CGovernanceVote& vote)
305 : EXCLUSIVE_LOCKS_REQUIRED(!cs_relay);
306 :
307 : // Notification interface trigger
308 : void UpdatedBlockTip(const CBlockIndex* pindex)
309 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay);
310 :
311 : // Signer interface
312 : bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus = false)
313 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
314 : std::shared_ptr<CGovernanceObject> FindGovernanceObjectByDataHash(const uint256& nDataHash)
315 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
316 : std::vector<std::shared_ptr<const CGovernanceObject>> GetApprovedProposals(const CDeterministicMNList& tip_mn_list)
317 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
318 : void AddGovernanceObject(CGovernanceObject& govobj, const std::string& peer_str)
319 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay);
320 :
321 : // Thread-safe accessors
322 : bool HaveObjectForHash(const uint256& nHash) const
323 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
324 : bool HaveVoteForHash(const uint256& nHash) const
325 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
326 : bool SerializeObjectForHash(const uint256& nHash, CDataStream& ss) const
327 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
328 : bool SerializeVoteForHash(const uint256& nHash, CDataStream& ss) const
329 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
330 : std::shared_ptr<CGovernanceObject> FindGovernanceObject(const uint256& nHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
331 : int GetVoteCount() const
332 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
333 : void AddPostponedObject(const CGovernanceObject& govobj)
334 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
335 :
336 : std::shared_ptr<const CGovernanceObject> FindConstGovernanceObject(const uint256& nHash) const
337 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
338 :
339 : // Used by NetGovernance
340 : std::vector<CInv> FetchRelayInventory() EXCLUSIVE_LOCKS_REQUIRED(!cs_relay);
341 : void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
342 : /** Get hashes of governance objects for which we have orphan votes. Also cleans up expired orphans. */
343 : [[nodiscard]] std::vector<uint256> GetOrphanVoteObjectHashes() EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
344 : std::pair<std::vector<uint256>, std::vector<uint256>> FetchGovernanceObjectVotes(
345 : size_t peers_per_hash_max, int64_t now, std::map<uint256, std::map<CService, int64_t>>& map_asked_recently) const
346 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
347 : /** Build bloom filter of existing votes for a governance object (for sync requests) */
348 : [[nodiscard]] CBloomFilter GetVoteBloomFilter(const uint256& nHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
349 : /** Returns inventory items for all syncable (non-deleted, non-expired) governance objects */
350 : [[nodiscard]] std::vector<CInv> GetSyncableObjectInvs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
351 : /** Returns inventory items for syncable votes on a specific object, filtered by bloom filter */
352 : [[nodiscard]] std::vector<CInv> GetSyncableVoteInvs(const uint256& nProp, const CBloomFilter& filter) const
353 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
354 : /// Called to indicate a requested object or vote has been received
355 : bool AcceptMessage(const uint256& nHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
356 : bool ProcessObject(const std::string& peer_str, const uint256& hash, CGovernanceObject& govobj)
357 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_store, !cs_relay);
358 :
359 : CDeterministicMNManager& GetMNManager();
360 : /** Process a governance vote. Returns true on success.
361 : * If the vote is for an unknown object (orphan), hashToRequest is set to the object hash. */
362 : bool ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception, uint256& hashToRequest)
363 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
364 :
365 :
366 : private:
367 : // Internal counterparts to "Thread-safe accessors"
368 : void AddPostponedObjectInternal(const CGovernanceObject& govobj)
369 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
370 : std::shared_ptr<CGovernanceObject> FindGovernanceObjectInternal(const uint256& nHash)
371 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
372 :
373 : std::shared_ptr<const CGovernanceObject> FindConstGovernanceObjectInternal(const uint256& nHash) const
374 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
375 :
376 : // Internal counterpart to "Signer interface"
377 : void AddGovernanceObjectInternal(CGovernanceObject& govobj, const std::string& peer_str)
378 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs_store, !cs_relay);
379 :
380 : // ...
381 : void MasternodeRateUpdate(const CGovernanceObject& govobj)
382 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
383 :
384 : bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus, bool fForce, bool& fRateCheckBypassed)
385 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
386 :
387 : void CheckPostponedObjects()
388 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs_store, !cs_relay);
389 :
390 : void InitOnLoad()
391 : EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
392 :
393 : void CheckOrphanVotes(CGovernanceObject& govobj)
394 : EXCLUSIVE_LOCKS_REQUIRED(cs_store, !cs_relay);
395 :
396 : void RebuildIndexes()
397 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
398 :
399 : void RemoveInvalidVotes()
400 : EXCLUSIVE_LOCKS_REQUIRED(cs_store);
401 : };
402 :
403 : #endif // BITCOIN_GOVERNANCE_GOVERNANCE_H
|