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_OBJECT_H
6 : #define BITCOIN_GOVERNANCE_OBJECT_H
7 :
8 : #include <governance/common.h>
9 : #include <governance/vote.h>
10 : #include <governance/votedb.h>
11 : #include <sync.h>
12 :
13 : #include <span.h>
14 :
15 : #include <exception>
16 : #include <iosfwd>
17 : #include <string>
18 :
19 : class CBLSPublicKey;
20 : class CDeterministicMNList;
21 : class ChainstateManager;
22 : class CMasternodeMetaMan;
23 : struct RPCResult;
24 :
25 : extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration)
26 :
27 : enum governance_exception_type_enum_t {
28 : /// Default value, normally indicates no exception condition occurred
29 : GOVERNANCE_EXCEPTION_NONE = 0,
30 : /// Unusual condition requiring no caller action
31 : GOVERNANCE_EXCEPTION_WARNING = 1,
32 : /// Requested operation cannot be performed
33 : GOVERNANCE_EXCEPTION_PERMANENT_ERROR = 2,
34 : /// Requested operation not currently possible, may resubmit later
35 : GOVERNANCE_EXCEPTION_TEMPORARY_ERROR = 3,
36 : /// Unexpected error (ie. should not happen unless there is a bug in the code)
37 : GOVERNANCE_EXCEPTION_INTERNAL_ERROR = 4
38 : };
39 :
40 : std::ostream& operator<<(std::ostream& os, governance_exception_type_enum_t eType);
41 :
42 : /**
43 : * A class which encapsulates information about a governance exception condition
44 : *
45 : * Derives from std::exception so is suitable for throwing
46 : * (ie. will be caught by a std::exception handler) but may also be used as a
47 : * normal object.
48 : */
49 : class CGovernanceException : public std::exception
50 : {
51 : private:
52 : std::string strMessage;
53 :
54 : governance_exception_type_enum_t eType;
55 :
56 : int nNodePenalty;
57 :
58 : public:
59 : explicit CGovernanceException(const std::string& strMessageIn = "",
60 : governance_exception_type_enum_t eTypeIn = GOVERNANCE_EXCEPTION_NONE,
61 : int nNodePenaltyIn = 0);
62 :
63 3040 : ~CGovernanceException() noexcept override = default;
64 :
65 0 : const char* what() const noexcept override
66 : {
67 0 : return strMessage.c_str();
68 : }
69 :
70 20 : const std::string& GetMessage() const
71 : {
72 20 : return strMessage;
73 : }
74 :
75 20 : governance_exception_type_enum_t GetType() const
76 : {
77 20 : return eType;
78 : }
79 :
80 0 : int GetNodePenalty() const
81 : {
82 0 : return nNodePenalty;
83 : }
84 : };
85 :
86 : static constexpr double GOVERNANCE_FILTER_FP_RATE = 0.001;
87 : static constexpr CAmount GOVERNANCE_PROPOSAL_FEE_TX = (1 * COIN);
88 : static constexpr int64_t GOVERNANCE_FEE_CONFIRMATIONS = 6;
89 : static constexpr int64_t GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS = 1;
90 : static constexpr int64_t GOVERNANCE_UPDATE_MIN = 60 * 60;
91 :
92 : // FOR SEEN MAP ARRAYS - GOVERNANCE OBJECTS AND VOTES
93 : enum class SeenObjectStatus {
94 : Valid = 0,
95 : ErrorInvalid,
96 : Executed,
97 : Unknown
98 : };
99 :
100 : using vote_time_pair_t = std::pair<CGovernanceVote, int64_t>;
101 :
102 0 : inline bool operator<(const vote_time_pair_t& p1, const vote_time_pair_t& p2)
103 : {
104 0 : return (p1.first < p2.first);
105 : }
106 :
107 : struct vote_instance_t {
108 : vote_outcome_enum_t eOutcome;
109 : int64_t nTime;
110 : int64_t nCreationTime;
111 :
112 5960 : explicit vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0, int64_t nCreationTimeIn = 0) :
113 2980 : eOutcome(eOutcomeIn),
114 2980 : nTime(nTimeIn),
115 2980 : nCreationTime(nCreationTimeIn)
116 2980 : {
117 5960 : }
118 :
119 3600 : SERIALIZE_METHODS(vote_instance_t, obj)
120 : {
121 : int nOutcome;
122 2400 : SER_WRITE(obj, nOutcome = int(obj.eOutcome));
123 1200 : READWRITE(nOutcome, obj.nTime, obj.nCreationTime);
124 1200 : SER_READ(obj, obj.eOutcome = vote_outcome_enum_t(nOutcome));
125 1200 : }
126 : };
127 :
128 : using vote_instance_m_t = std::map<int, vote_instance_t>;
129 :
130 : struct vote_rec_t {
131 : vote_instance_m_t mapInstances;
132 :
133 3600 : SERIALIZE_METHODS(vote_rec_t, obj)
134 : {
135 1200 : READWRITE(obj.mapInstances);
136 1200 : }
137 : };
138 :
139 : /**
140 : * Governance Object
141 : *
142 : */
143 :
144 : class CGovernanceObject
145 : {
146 : public: // Types
147 : using vote_m_t = std::map<COutPoint, vote_rec_t>;
148 :
149 : public:
150 : /// critical section to protect the inner data structures
151 : mutable Mutex cs;
152 :
153 : private:
154 : Governance::Object m_obj;
155 :
156 : /// time this object was marked for deletion
157 0 : int64_t nDeletionTime GUARDED_BY(cs){0};
158 :
159 : /// is valid by blockchain
160 0 : bool fCachedLocalValidity{false};
161 : std::string strLocalValidityError;
162 :
163 : // VARIOUS FLAGS FOR OBJECT / SET VIA MASTERNODE VOTING
164 :
165 : /// true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though)
166 0 : bool fCachedFunding{false};
167 :
168 : /// true == minimum network has been reached flagging this object as a valid and understood governance object (e.g, the serialized data is correct format, etc)
169 0 : bool fCachedValid{true};
170 :
171 : /// true == minimum network support has been reached saying this object should be deleted from the system entirely
172 0 : bool fCachedDelete{false};
173 :
174 : /** true == minimum network support has been reached flagging this object as endorsed by an elected representative body
175 : * (e.g. business review board / technical review board /etc)
176 : */
177 0 : bool fCachedEndorsed{false};
178 :
179 : /// object was updated and cached values should be updated soon
180 0 : bool fDirtyCache{true};
181 :
182 : /// Object is no longer of interest
183 0 : bool fExpired GUARDED_BY(cs){false};
184 :
185 : /// Failed to parse object data
186 0 : bool fUnparsable{false};
187 :
188 : vote_m_t mapCurrentMNVotes GUARDED_BY(cs);
189 :
190 : CGovernanceObjectVoteFile fileVotes GUARDED_BY(cs);
191 :
192 : public:
193 : CGovernanceObject();
194 : CGovernanceObject(const uint256& nHashParentIn, int nRevisionIn, int64_t nTime, const uint256& nCollateralHashIn, const std::string& strDataHexIn);
195 : CGovernanceObject(const CGovernanceObject& other);
196 : template <typename Stream>
197 0 : CGovernanceObject(deserialize_type, Stream& s) { s >> *this; }
198 :
199 : // Getters
200 18340 : bool IsSetCachedFunding() const { return fCachedFunding; }
201 48988 : bool IsSetCachedValid() const { return fCachedValid; }
202 51829 : bool IsSetCachedDelete() const { return fCachedDelete; }
203 18048 : bool IsSetCachedEndorsed() const { return fCachedEndorsed; }
204 12926 : bool IsSetDirtyCache() const { return fDirtyCache; }
205 25482 : bool IsSetExpired() const EXCLUSIVE_LOCKS_REQUIRED(!cs)
206 : {
207 50964 : return WITH_LOCK(cs, return fExpired);
208 : }
209 91468 : GovernanceObject GetObjectType() const { return m_obj.type; }
210 49461 : int64_t GetCreationTime() const { return m_obj.time; }
211 25852 : int64_t GetDeletionTime() const EXCLUSIVE_LOCKS_REQUIRED(!cs)
212 : {
213 51704 : return WITH_LOCK(cs, return nDeletionTime);
214 : }
215 :
216 3594 : const CGovernanceObjectVoteFile& GetVoteFile() const EXCLUSIVE_LOCKS_REQUIRED(cs)
217 : {
218 3594 : AssertLockHeld(cs);
219 3594 : return fileVotes;
220 : }
221 18501 : const COutPoint& GetMasternodeOutpoint() const { return m_obj.masternodeOutpoint; }
222 300 : const Governance::Object& Object() const { return m_obj; }
223 18048 : const uint256& GetCollateralHash() const { return m_obj.collateralHash; }
224 :
225 : // Setters
226 104 : void SetExpired() EXCLUSIVE_LOCKS_REQUIRED(!cs)
227 : {
228 208 : WITH_LOCK(cs, fExpired = true);
229 104 : }
230 : void SetMasternodeOutpoint(const COutPoint& outpoint);
231 : void SetSignature(Span<const uint8_t> sig);
232 :
233 : // Signature related functions
234 : bool CheckSignature(const CBLSPublicKey& pubKey) const;
235 : uint256 GetSignatureHash() const;
236 :
237 : // CORE OBJECT FUNCTIONS
238 :
239 : bool IsValidLocally(const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman, std::string& strError, bool fCheckCollateral) const
240 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
241 :
242 : bool IsValidLocally(const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman, std::string& strError, bool& fMissingConfirmations, bool fCheckCollateral) const
243 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
244 :
245 : /// Check the collateral transaction for the budget proposal/finalized budget
246 : bool IsCollateralValid(const ChainstateManager& chainman, std::string& strError, bool& fMissingConfirmations) const
247 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
248 :
249 : void UpdateLocalValidity(const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman)
250 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
251 :
252 : void UpdateSentinelVariables(const CDeterministicMNList& tip_mn_list)
253 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
254 :
255 342 : void PrepareDeletion(int64_t nDeletionTime_) EXCLUSIVE_LOCKS_REQUIRED(!cs)
256 : {
257 342 : fCachedDelete = true;
258 342 : LOCK(cs);
259 342 : if (nDeletionTime == 0) {
260 144 : nDeletionTime = nDeletionTime_;
261 144 : }
262 342 : }
263 :
264 : CAmount GetMinCollateralFee() const;
265 :
266 : UniValue GetJSONObject() const;
267 :
268 : uint256 GetHash() const;
269 : uint256 GetDataHash() const;
270 :
271 : // GET VOTE COUNT FOR SIGNAL
272 :
273 : int CountMatchingVotes(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const
274 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
275 :
276 : int GetAbsoluteYesCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
277 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
278 : int GetAbsoluteNoCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
279 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
280 : int GetYesCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
281 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
282 : int GetNoCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
283 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
284 : int GetAbstainCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
285 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
286 :
287 0 : struct UniqueVoterCount {
288 0 : uint16_t m_regular{0};
289 0 : uint16_t m_evo{0};
290 : };
291 : UniqueVoterCount GetUniqueVoterCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const
292 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
293 :
294 : bool GetCurrentMNVotes(const COutPoint& mnCollateralOutpoint, vote_rec_t& voteRecord) const
295 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
296 :
297 : // FUNCTIONS FOR DEALING WITH DATA STRING
298 :
299 : std::string GetDataAsHexString() const;
300 : std::string GetDataAsPlainString() const;
301 :
302 : // SERIALIZER
303 :
304 : template<typename Stream>
305 896 : void Serialize(Stream& s) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
306 : {
307 : // SERIALIZE DATA FOR SAVING/LOADING OR NETWORK FUNCTIONS
308 896 : s << m_obj;
309 896 : if (s.GetType() & SER_DISK) {
310 : // Only include these for the disk file format
311 240 : LOCK(cs);
312 240 : s << nDeletionTime << fExpired << mapCurrentMNVotes << fileVotes;
313 240 : }
314 896 : }
315 :
316 : template<typename Stream>
317 248 : void Unserialize(Stream& s) EXCLUSIVE_LOCKS_REQUIRED(!cs)
318 : {
319 248 : s >> m_obj;
320 248 : if (s.GetType() & SER_DISK) {
321 : // Only include these for the disk file format
322 0 : LOCK(cs);
323 0 : s >> nDeletionTime >> fExpired >> mapCurrentMNVotes >> fileVotes;
324 0 : }
325 : // AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY
326 248 : }
327 :
328 : // JSON emitters/help
329 : [[nodiscard]] static RPCResult GetInnerJsonHelp(const std::string& key, bool optional);
330 : [[nodiscard]] UniValue GetInnerJson() const;
331 :
332 : [[nodiscard]] static RPCResult GetStateJsonHelp(const std::string& key, bool optional, const std::string& local_valid_key);
333 : [[nodiscard]] UniValue GetStateJson(const ChainstateManager& chainman, const CDeterministicMNList& tip_mn_list, const std::string& local_valid_key) const
334 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
335 :
336 : [[nodiscard]] static RPCResult GetVotesJsonHelp(const std::string& key, bool optional);
337 : [[nodiscard]] UniValue GetVotesJson(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t signal) const
338 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
339 :
340 : // FUNCTIONS FOR DEALING WITH DATA STRING
341 : void LoadData();
342 : void GetData(UniValue& objResult) const;
343 :
344 : bool ProcessVote(CMasternodeMetaMan& mn_metaman, bool fRateChecksEnabled, const CDeterministicMNList& tip_mn_list,
345 : const CGovernanceVote& vote, CGovernanceException& exception) EXCLUSIVE_LOCKS_REQUIRED(!cs);
346 :
347 : /// Called when MN's which have voted on this object have been removed
348 : void ClearMasternodeVotes(const CDeterministicMNList& tip_mn_list)
349 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
350 :
351 : // Revalidate all votes from this MN and delete them if validation fails.
352 : // This is the case for DIP3 MNs that changed voting or operator keys and
353 : // also for MNs that were removed from the list completely.
354 : // Returns deleted vote hashes.
355 : std::set<uint256> RemoveInvalidVotes(const CDeterministicMNList& tip_mn_list, const COutPoint& mnOutpoint)
356 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
357 : };
358 :
359 : namespace governance {
360 : /**
361 : * Validate the serialized proposal data (hex-encoded JSON).
362 : * Returns true on success. On failure, returns false and fills strErrorOut
363 : * with a semicolon-delimited list of detected issues.
364 : */
365 : bool ValidateProposal(const std::string& strDataHex, std::string& strErrorOut,
366 : bool fCheckExpiration = true, bool fAllowScript = true);
367 : } // namespace governance
368 :
369 : #endif // BITCOIN_GOVERNANCE_OBJECT_H
|