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_SPORK_H
6 : #define BITCOIN_SPORK_H
7 :
8 : #include <hash.h>
9 : #include <key.h>
10 : #include <pubkey.h>
11 : #include <saltedhasher.h>
12 : #include <sync.h>
13 :
14 : #include <array>
15 : #include <optional>
16 : #include <string_view>
17 : #include <unordered_map>
18 : #include <vector>
19 :
20 : template<typename T>
21 : class CFlatDB;
22 : class CDataStream;
23 : class uint256;
24 : class CInv;
25 :
26 : class CSporkMessage;
27 : class CSporkManager;
28 :
29 : /*
30 : Don't ever reuse these IDs for other sporks
31 : - This would result in old clients getting confused about which spork is for what
32 : */
33 : enum SporkId : int32_t {
34 : SPORK_2_INSTANTSEND_ENABLED = 10001,
35 : SPORK_17_QUORUM_DKG_ENABLED = 10016,
36 : SPORK_19_CHAINLOCKS_ENABLED = 10018,
37 : SPORK_21_QUORUM_ALL_CONNECTED = 10020,
38 : SPORK_23_QUORUM_POSE = 10022,
39 : // SPORK_24_DEPRECATED = 10023,
40 :
41 : SPORK_INVALID = -1,
42 : };
43 : template<> struct is_serializable_enum<SporkId> : std::true_type {};
44 :
45 : namespace std
46 : {
47 : template<> struct hash<SporkId>
48 : {
49 13267303 : std::size_t operator()(SporkId const& id) const noexcept
50 : {
51 13267303 : return std::hash<int>{}(id);
52 : }
53 : };
54 : }
55 :
56 : using SporkValue = int64_t;
57 : struct CSporkDef
58 : {
59 : SporkId sporkId{SPORK_INVALID};
60 : SporkValue defaultValue{0};
61 : std::string_view name;
62 : };
63 :
64 : #define MAKE_SPORK_DEF(name, defaultValue) CSporkDef{name, defaultValue, #name}
65 : [[maybe_unused]] static constexpr std::array<CSporkDef, 5> sporkDefs = {
66 : MAKE_SPORK_DEF(SPORK_2_INSTANTSEND_ENABLED, 4070908800ULL), // OFF
67 : MAKE_SPORK_DEF(SPORK_17_QUORUM_DKG_ENABLED, 4070908800ULL), // OFF
68 : MAKE_SPORK_DEF(SPORK_19_CHAINLOCKS_ENABLED, 4070908800ULL), // OFF
69 : MAKE_SPORK_DEF(SPORK_21_QUORUM_ALL_CONNECTED, 4070908800ULL), // OFF
70 : MAKE_SPORK_DEF(SPORK_23_QUORUM_POSE, 4070908800ULL), // OFF
71 : };
72 : #undef MAKE_SPORK_DEF
73 :
74 : /**
75 : * Sporks are network parameters used primarily to prevent forking and turn
76 : * on/off certain features. They are a soft consensus mechanism.
77 : *
78 : * We use 2 main classes to manage the spork system.
79 : *
80 : * SporkMessages - low-level constructs which contain the sporkID, value,
81 : * signature and a signature timestamp
82 : * SporkManager - a higher-level construct which manages the naming, use of
83 : * sporks, signatures and verification, and which sporks are active according
84 : * to this node
85 : */
86 :
87 : /**
88 : * CSporkMessage is a low-level class used to encapsulate Spork messages and
89 : * serialize them for transmission to other peers. This includes the internal
90 : * spork ID, value, spork signature and timestamp for the signature.
91 : */
92 : class CSporkMessage
93 : {
94 : private:
95 : std::vector<unsigned char> vchSig;
96 :
97 : public:
98 11562 : SporkId nSporkID{0};
99 11562 : SporkValue nValue{0};
100 11562 : int64_t nTimeSigned{0};
101 :
102 810 : CSporkMessage(SporkId nSporkID, SporkValue nValue, int64_t nTimeSigned) :
103 405 : nSporkID(nSporkID),
104 405 : nValue(nValue),
105 405 : nTimeSigned(nTimeSigned)
106 810 : {}
107 :
108 34686 : CSporkMessage() = default;
109 :
110 68184 : SERIALIZE_METHODS(CSporkMessage, obj)
111 : {
112 22728 : READWRITE(obj.nSporkID, obj.nValue, obj.nTimeSigned, obj.vchSig);
113 22728 : }
114 :
115 : /**
116 : * GetHash returns the double-sha256 hash of the serialized spork message.
117 : */
118 : uint256 GetHash() const;
119 :
120 : /**
121 : * GetSignatureHash returns the hash of the serialized spork message
122 : * without the signature included. The intent of this method is to get the
123 : * hash to be signed.
124 : */
125 : uint256 GetSignatureHash() const;
126 :
127 : /**
128 : * Sign will sign the spork message with the given key.
129 : */
130 : bool Sign(const CKey& key);
131 :
132 : /**
133 : * CheckSignature will ensure the spork signature matches the provided public
134 : * key hash.
135 : */
136 : bool CheckSignature(const CKeyID& pubKeyId) const;
137 :
138 : /**
139 : * GetSignerKeyID is used to recover the spork address of the key used to
140 : * sign this spork message.
141 : *
142 : * This method was introduced along with the multi-signer sporks feature,
143 : * in order to identify which spork key signed this message.
144 : */
145 : std::optional<CKeyID> GetSignerKeyID() const;
146 : };
147 :
148 : class SporkStore
149 : {
150 : protected:
151 : static const std::string SERIALIZATION_VERSION_STRING;
152 :
153 : mutable Mutex cs;
154 :
155 : Uint256HashMap<CSporkMessage> mapSporksByHash GUARDED_BY(cs);
156 : std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage> > mapSporksActive GUARDED_BY(cs);
157 :
158 : public:
159 : template<typename Stream>
160 2897 : void Serialize(Stream &s) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
161 : {
162 : // We don't serialize pubkey ids because pubkeys should be
163 : // hardcoded or be set with cmdline or options, should
164 : // not reuse pubkeys from previous dashd run.
165 : // We don't serialize private key to prevent its leakage.
166 2897 : LOCK(cs);
167 2897 : s << SERIALIZATION_VERSION_STRING << mapSporksByHash << mapSporksActive;
168 2897 : }
169 :
170 : template<typename Stream>
171 2722 : void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs)
172 : {
173 2722 : LOCK(cs);
174 2722 : std::string strVersion;
175 2722 : s >> strVersion;
176 2722 : if (strVersion != SERIALIZATION_VERSION_STRING) {
177 0 : return;
178 : }
179 2722 : s >> mapSporksByHash >> mapSporksActive;
180 2722 : }
181 :
182 : /**
183 : * Clear is used to clear all in-memory active spork messages. Since spork
184 : * public and private keys are set in init.cpp, we do not clear them here.
185 : *
186 : * This method was introduced along with the spork cache.
187 : */
188 : void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs);
189 :
190 : /**
191 : * ToString returns the string representation of the SporkManager.
192 : */
193 : std::string ToString() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
194 : };
195 :
196 : /**
197 : * CSporkManager is a higher-level class which manages the node's spork
198 : * messages, rules for which sporks should be considered active/inactive, and
199 : * processing for certain sporks (e.g. spork 12).
200 : */
201 : class CSporkManager : public SporkStore
202 : {
203 : private:
204 : using db_type = CFlatDB<SporkStore>;
205 :
206 : private:
207 : const std::unique_ptr<db_type> m_db;
208 : bool is_valid{false};
209 :
210 : // TODO: drop mutex cs_cache completely so far as sporks are used on testnet only
211 : // and simplify IsSporkActive to avoid any mutex for better mainnet performance
212 : mutable Mutex cs_cache;
213 : mutable std::unordered_map<SporkId, bool> mapSporksCachedActive GUARDED_BY(cs_cache);
214 : mutable std::unordered_map<SporkId, SporkValue> mapSporksCachedValues GUARDED_BY(cs_cache);
215 :
216 : std::set<CKeyID> setSporkPubKeyIDs GUARDED_BY(cs);
217 : int nMinSporkKeys GUARDED_BY(cs) {std::numeric_limits<int>::max()};
218 : CKey sporkPrivKey GUARDED_BY(cs);
219 :
220 : /**
221 : * SporkValueIfActive is used to get the value agreed upon by the majority
222 : * of signed spork messages for a given Spork ID.
223 : */
224 : std::optional<SporkValue> SporkValueIfActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(cs, !cs_cache);
225 :
226 : public:
227 : CSporkManager(const CSporkManager&) = delete;
228 : CSporkManager& operator=(const CSporkManager&) = delete;
229 : CSporkManager();
230 : ~CSporkManager();
231 :
232 : bool LoadCache();
233 :
234 : bool IsValid() const { return is_valid; }
235 :
236 : /**
237 : * CheckAndRemove is defined to fulfill an interface as part of the on-disk
238 : * cache used to cache sporks between runs. If sporks that are restored
239 : * from cache do not have valid signatures when compared against the
240 : * current spork private keys, they are removed from in-memory storage.
241 : *
242 : * This method was introduced along with the spork cache.
243 : */
244 : void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs);
245 :
246 : /**
247 : * GetValidSporkSigner validates signed time and recovers the signer pubkey.
248 : * Returns the signer's CKeyID on success, or std::nullopt if the spork is invalid
249 : * (peer should be punished in that case).
250 : */
251 : [[nodiscard]] std::optional<CKeyID> GetValidSporkSigner(const CSporkMessage& spork) const
252 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
253 : /**
254 : * ProcessSpork adds the spork to local state. Returns true if the spork was new or
255 : * updated and should be relayed. `keyIDSigner` must be the signer key previously
256 : * recovered via GetValidSporkSigner. `peer_log_suffix` is appended to log lines for
257 : * cross-referencing with the source peer (e.g. " peer=42").
258 : */
259 : [[nodiscard]] bool ProcessSpork(const CSporkMessage& spork, const CKeyID& keyIDSigner,
260 : std::string_view peer_log_suffix = {})
261 : EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache);
262 :
263 : /**
264 : * ActiveSporks returns a snapshot of currently active sporks indexed by SporkId then
265 : * signer CKeyID. Used by net_processing to answer the 'getsporks' p2p message.
266 : */
267 : std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage>> ActiveSporks() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
268 :
269 : /**
270 : * UpdateSpork is used by the spork RPC command to set a new spork value, sign
271 : * and return the spork message, ready for network relay.
272 : * It returns nullopt if nothing to relay
273 : */
274 : std::optional<CInv> UpdateSpork(SporkId nSporkID, SporkValue nValue) EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache);
275 :
276 : /**
277 : * IsSporkActive returns a bool for time-based sporks, and should be used
278 : * to determine whether the spork can be considered active or not.
279 : * For value-based sporks such as SPORK_5_INSTANTSEND_MAX_VALUE, the spork
280 : * value should not be considered a timestamp, but an integer value
281 : * instead, and therefore this method doesn't make sense and should not be
282 : * used.
283 : */
284 : bool IsSporkActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
285 :
286 : /**
287 : * GetSporkValue returns the spork value given a Spork ID. If no active spork
288 : * message has yet been received by the node, it returns the default value.
289 : */
290 : SporkValue GetSporkValue(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache);
291 :
292 : /**
293 : * GetSporkIDByName returns the internal Spork ID given the spork name.
294 : */
295 : static SporkId GetSporkIDByName(std::string_view strName);
296 :
297 : /**
298 : * GetSporkByHash returns a spork message given a hash of the spork message.
299 : *
300 : * This is used when a requesting peer sends a MSG_SPORK inventory message with
301 : * the hash, to quickly lookup and return the full spork message. We maintain a
302 : * hash-based index of sporks for this reason, and this function is the access
303 : * point into that index.
304 : */
305 : std::optional<CSporkMessage> GetSporkByHash(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
306 :
307 : /**
308 : * SetSporkAddress is used to set a public key ID which will be used to
309 : * verify spork signatures.
310 : *
311 : * This can be called multiple times to add multiple keys to the set of
312 : * valid spork signers.
313 : */
314 : bool SetSporkAddress(const std::string& strAddress) EXCLUSIVE_LOCKS_REQUIRED(!cs);
315 :
316 : /**
317 : * SetMinSporkKeys is used to set the required spork signer threshold, for
318 : * a spork to be considered active.
319 : *
320 : * This value must be at least a majority of the total number of spork
321 : * keys, and for obvious reasons cannot be larger than that number.
322 : */
323 : bool SetMinSporkKeys(int minSporkKeys) EXCLUSIVE_LOCKS_REQUIRED(!cs);
324 :
325 : /**
326 : * SetPrivKey is used to set a spork key to enable setting / signing of
327 : * spork values.
328 : *
329 : * This will return false if the private key does not match any spork
330 : * address in the set of valid spork signers (see SetSporkAddress).
331 : */
332 : bool SetPrivKey(const std::string& strPrivKey) EXCLUSIVE_LOCKS_REQUIRED(!cs);
333 : };
334 :
335 : #endif // BITCOIN_SPORK_H
|