Line data Source code
1 : // Copyright (c) 2014-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 : #include <spork.h>
6 :
7 : #include <chainparams.h>
8 : #include <flat-database.h>
9 : #include <key_io.h>
10 : #include <logging.h>
11 : #include <messagesigner.h>
12 : #include <protocol.h>
13 : #include <script/standard.h>
14 : #include <timedata.h>
15 : #include <util/helpers.h>
16 : #include <util/message.h> // for MESSAGE_MAGIC
17 : #include <util/string.h>
18 :
19 : #include <ranges>
20 : #include <string>
21 :
22 3308 : const std::string SporkStore::SERIALIZATION_VERSION_STRING = "CSporkManager-Version-2";
23 :
24 6262516 : std::optional<SporkValue> CSporkManager::SporkValueIfActive(SporkId nSporkID) const
25 : {
26 6262516 : AssertLockHeld(cs);
27 :
28 6262516 : if (!mapSporksActive.count(nSporkID)) return std::nullopt;
29 :
30 : {
31 607582 : LOCK(cs_cache);
32 607582 : if (auto it = mapSporksCachedValues.find(nSporkID); it != mapSporksCachedValues.end()) {
33 603248 : return {it->second};
34 : }
35 607582 : }
36 :
37 : // calc how many values we have and how many signers vote for every value
38 4334 : std::unordered_map<SporkValue, int> mapValueCounts;
39 10029 : for (const auto& [_, spork] : mapSporksActive.at(nSporkID)) {
40 5695 : mapValueCounts[spork.nValue]++;
41 5695 : if (mapValueCounts.at(spork.nValue) >= nMinSporkKeys) {
42 : // nMinSporkKeys is always more than the half of the max spork keys number,
43 : // so there is only one such value and we can stop here
44 : {
45 3192 : LOCK(cs_cache);
46 3192 : mapSporksCachedValues[nSporkID] = spork.nValue;
47 3192 : }
48 3192 : return {spork.nValue};
49 : }
50 : }
51 :
52 1142 : return std::nullopt;
53 6262516 : }
54 :
55 0 : void SporkStore::Clear()
56 : {
57 0 : LOCK(cs);
58 0 : mapSporksActive.clear();
59 0 : mapSporksByHash.clear();
60 : // sporkPubKeyID and sporkPrivKey should be set in init.cpp,
61 : // we should not alter them here.
62 0 : }
63 :
64 7124 : CSporkManager::CSporkManager() :
65 3562 : m_db{std::make_unique<db_type>("sporks.dat", "magicSporkCache")}
66 3562 : {
67 3562 : }
68 :
69 7122 : CSporkManager::~CSporkManager()
70 3561 : {
71 3561 : if (!is_valid) return;
72 2897 : m_db->Store(*this);
73 7122 : }
74 :
75 2897 : bool CSporkManager::LoadCache()
76 : {
77 2897 : assert(m_db != nullptr);
78 2897 : is_valid = m_db->Load(*this);
79 2897 : if (is_valid) {
80 2897 : CheckAndRemove();
81 2897 : }
82 2897 : return is_valid;
83 : }
84 :
85 2897 : void CSporkManager::CheckAndRemove()
86 : {
87 2897 : LOCK(cs);
88 :
89 2897 : if (setSporkPubKeyIDs.empty()) return;
90 :
91 3963 : for (auto itActive = mapSporksActive.begin(); itActive != mapSporksActive.end();) {
92 1066 : auto itSignerPair = itActive->second.begin();
93 2174 : while (itSignerPair != itActive->second.end()) {
94 2156 : bool fHasValidSig = setSporkPubKeyIDs.find(itSignerPair->first) != setSporkPubKeyIDs.end() &&
95 1048 : itSignerPair->second.CheckSignature(itSignerPair->first);
96 1108 : if (!fHasValidSig) {
97 60 : mapSporksByHash.erase(itSignerPair->second.GetHash());
98 60 : itActive->second.erase(itSignerPair++);
99 60 : continue;
100 : }
101 1048 : ++itSignerPair;
102 : }
103 1066 : if (itActive->second.empty()) {
104 18 : mapSporksActive.erase(itActive++);
105 18 : continue;
106 : }
107 1048 : ++itActive;
108 : }
109 :
110 4132 : for (auto itByHash = mapSporksByHash.begin(); itByHash != mapSporksByHash.end();) {
111 1235 : bool found = false;
112 1238 : for (const auto& signer : setSporkPubKeyIDs) {
113 1235 : if (itByHash->second.CheckSignature(signer)) {
114 1232 : found = true;
115 1232 : break;
116 : }
117 : }
118 1235 : if (!found) {
119 3 : mapSporksByHash.erase(itByHash++);
120 3 : continue;
121 : }
122 1232 : ++itByHash;
123 : }
124 2897 : }
125 :
126 2391 : std::optional<CKeyID> CSporkManager::GetValidSporkSigner(const CSporkMessage& spork) const
127 : {
128 2391 : if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) {
129 0 : LogPrint(BCLog::SPORK, "CSporkManager::%s -- ERROR: too far into the future\n", __func__);
130 0 : return std::nullopt;
131 : }
132 :
133 2391 : auto opt_keyIDSigner = spork.GetSignerKeyID();
134 :
135 4782 : if (opt_keyIDSigner == std::nullopt || WITH_LOCK(cs, return !setSporkPubKeyIDs.count(*opt_keyIDSigner))) {
136 0 : LogPrint(BCLog::SPORK, "CSporkManager::%s -- ERROR: invalid signature\n", __func__);
137 0 : return std::nullopt;
138 : }
139 2391 : return opt_keyIDSigner;
140 2391 : }
141 :
142 2391 : bool CSporkManager::ProcessSpork(const CSporkMessage& spork, const CKeyID& keyIDSigner, std::string_view peer_log_suffix)
143 : {
144 2391 : uint256 hash = spork.GetHash();
145 4782 : std::string strLogMsg{strprintf("SPORK -- hash: %s id: %d value: %10d%s", hash.ToString(), spork.nSporkID,
146 2391 : spork.nValue, peer_log_suffix)};
147 :
148 : {
149 2391 : LOCK(cs); // make sure to not lock this together with cs_main
150 2391 : if (mapSporksActive.count(spork.nSporkID)) {
151 1111 : if (mapSporksActive[spork.nSporkID].count(keyIDSigner)) {
152 967 : if (mapSporksActive[spork.nSporkID][keyIDSigner].nTimeSigned >= spork.nTimeSigned) {
153 409 : LogPrint(BCLog::SPORK, "%s seen\n", strLogMsg);
154 409 : return false;
155 : } else {
156 558 : LogPrintf("%s updated\n", strLogMsg);
157 : }
158 558 : } else {
159 144 : LogPrintf("%s new signer\n", strLogMsg);
160 : }
161 702 : } else {
162 1280 : LogPrintf("%s new\n", strLogMsg);
163 : }
164 :
165 1982 : mapSporksByHash[hash] = spork;
166 1982 : mapSporksActive[spork.nSporkID][keyIDSigner] = spork;
167 : // Clear cached values on new spork being processed
168 : {
169 1982 : LOCK(cs_cache);
170 1982 : mapSporksCachedActive.erase(spork.nSporkID);
171 1982 : mapSporksCachedValues.erase(spork.nSporkID);
172 1982 : }
173 2391 : }
174 :
175 1982 : return true;
176 2391 : }
177 :
178 941 : std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage>> CSporkManager::ActiveSporks() const
179 : {
180 941 : LOCK(cs); // make sure to not lock this together with cs_main
181 941 : return mapSporksActive;
182 941 : }
183 :
184 405 : std::optional<CInv> CSporkManager::UpdateSpork(SporkId nSporkID, SporkValue nValue)
185 : {
186 405 : CSporkMessage spork(nSporkID, nValue, GetAdjustedTime());
187 :
188 405 : LOCK(cs);
189 :
190 405 : if (!spork.Sign(sporkPrivKey)) {
191 0 : LogPrintf("CSporkManager::%s -- ERROR: signing failed for spork %d\n", __func__, nSporkID);
192 0 : return std::nullopt;
193 : }
194 :
195 405 : auto opt_keyIDSigner = spork.GetSignerKeyID();
196 405 : if (opt_keyIDSigner == std::nullopt || !setSporkPubKeyIDs.count(*opt_keyIDSigner)) {
197 0 : LogPrintf("CSporkManager::UpdateSpork: failed to find keyid for private key\n");
198 0 : return std::nullopt;
199 : }
200 :
201 405 : LogPrintf("CSporkManager::%s -- signed %d %s\n", __func__, nSporkID, spork.GetHash().ToString());
202 :
203 405 : mapSporksByHash[spork.GetHash()] = spork;
204 405 : mapSporksActive[nSporkID][*opt_keyIDSigner] = spork;
205 : // Clear cached values on new spork being processed
206 :
207 405 : LOCK(cs_cache);
208 405 : mapSporksCachedActive.erase(spork.nSporkID);
209 405 : mapSporksCachedValues.erase(spork.nSporkID);
210 :
211 :
212 405 : CInv inv(MSG_SPORK, spork.GetHash());
213 405 : return inv;
214 405 : }
215 :
216 6373034 : bool CSporkManager::IsSporkActive(SporkId nSporkID) const
217 : {
218 : // If nSporkID is cached, and the cached value is true, then return early true
219 : {
220 6373034 : LOCK(cs_cache);
221 6373034 : if (auto it = mapSporksCachedActive.find(nSporkID); it != mapSporksCachedActive.end() && it->second) {
222 2369948 : return true;
223 : }
224 6373034 : }
225 :
226 4003086 : SporkValue nSporkValue = GetSporkValue(nSporkID);
227 : // Get time is somewhat costly it looks like
228 4003086 : bool ret = nSporkValue < GetAdjustedTime();
229 : // Only cache true values
230 4003086 : if (ret) {
231 2475 : LOCK(cs_cache);
232 2475 : mapSporksCachedActive[nSporkID] = ret;
233 2475 : }
234 4003086 : return ret;
235 6373034 : }
236 :
237 6262525 : SporkValue CSporkManager::GetSporkValue(SporkId nSporkID) const
238 : {
239 : // Harden all sporks on Mainnet
240 6262525 : if (!Params().IsTestChain()) {
241 9 : switch (nSporkID) {
242 : case SPORK_21_QUORUM_ALL_CONNECTED:
243 0 : return 1;
244 : default:
245 9 : return 0;
246 : }
247 : }
248 :
249 6262516 : LOCK(cs);
250 :
251 6262516 : if (auto opt_sporkValue = SporkValueIfActive(nSporkID)) {
252 606440 : return *opt_sporkValue;
253 : }
254 :
255 :
256 5656076 : if (auto optSpork = util::find_if_opt(sporkDefs,
257 19243227 : [&nSporkID](const auto& sporkDef) { return sporkDef.sporkId == nSporkID; })) {
258 5656076 : return optSpork->defaultValue;
259 : } else {
260 0 : LogPrint(BCLog::SPORK, "CSporkManager::GetSporkValue -- Unknown Spork ID %d\n", nSporkID);
261 0 : return -1;
262 : }
263 6262525 : }
264 :
265 405 : SporkId CSporkManager::GetSporkIDByName(std::string_view strName)
266 : {
267 405 : if (auto optSpork = util::find_if_opt(sporkDefs,
268 1287 : [&strName](const auto& sporkDef) { return sporkDef.name == strName; })) {
269 405 : return optSpork->sporkId;
270 : }
271 :
272 0 : LogPrint(BCLog::SPORK, "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
273 0 : return SPORK_INVALID;
274 405 : }
275 :
276 6990 : std::optional<CSporkMessage> CSporkManager::GetSporkByHash(const uint256& hash) const
277 : {
278 6990 : LOCK(cs);
279 :
280 6990 : if (const auto it = mapSporksByHash.find(hash); it != mapSporksByHash.end())
281 3432 : return {it->second};
282 :
283 3558 : return std::nullopt;
284 6990 : }
285 :
286 3043 : bool CSporkManager::SetSporkAddress(const std::string& strAddress)
287 : {
288 3043 : LOCK(cs);
289 3043 : CTxDestination dest = DecodeDestination(strAddress);
290 3043 : const PKHash* pkhash = std::get_if<PKHash>(&dest);
291 3043 : if (!pkhash) {
292 0 : LogPrintf("CSporkManager::SetSporkAddress -- Failed to parse spork address\n");
293 0 : return false;
294 : }
295 3043 : setSporkPubKeyIDs.insert(ToKeyID(*pkhash));
296 3043 : return true;
297 3043 : }
298 :
299 2935 : bool CSporkManager::SetMinSporkKeys(int minSporkKeys)
300 : {
301 2935 : LOCK(cs);
302 2935 : if (int maxKeysNumber = setSporkPubKeyIDs.size(); (minSporkKeys <= maxKeysNumber / 2) || (minSporkKeys > maxKeysNumber)) {
303 0 : LogPrintf("CSporkManager::SetMinSporkKeys -- Invalid min spork signers number: %d\n", minSporkKeys);
304 0 : return false;
305 : }
306 2935 : nMinSporkKeys = minSporkKeys;
307 2935 : return true;
308 2935 : }
309 :
310 291 : bool CSporkManager::SetPrivKey(const std::string& strPrivKey)
311 : {
312 291 : CKey key;
313 291 : CPubKey pubKey;
314 291 : if (!CMessageSigner::GetKeysFromSecret(strPrivKey, key, pubKey)) {
315 0 : LogPrintf("CSporkManager::SetPrivKey -- Failed to parse private key\n");
316 0 : return false;
317 : }
318 :
319 291 : LOCK(cs);
320 291 : if (setSporkPubKeyIDs.find(pubKey.GetID()) == setSporkPubKeyIDs.end()) {
321 0 : LogPrintf("CSporkManager::SetPrivKey -- New private key does not belong to spork addresses\n");
322 0 : return false;
323 : }
324 :
325 291 : if (!CSporkMessage().Sign(key)) {
326 0 : LogPrintf("CSporkManager::SetPrivKey -- Test signing failed\n");
327 0 : return false;
328 : }
329 :
330 : // Test signing successful, proceed
331 291 : LogPrintf("CSporkManager::SetPrivKey -- Successfully initialized as spork signer\n");
332 291 : sporkPrivKey = key;
333 291 : return true;
334 291 : }
335 :
336 5619 : std::string SporkStore::ToString() const
337 : {
338 5619 : LOCK(cs);
339 5619 : return strprintf("Sporks: %llu", mapSporksActive.size());
340 5619 : }
341 :
342 6735 : uint256 CSporkMessage::GetHash() const
343 : {
344 6735 : return SerializeHash(*this);
345 : }
346 :
347 0 : uint256 CSporkMessage::GetSignatureHash() const
348 : {
349 0 : CHashWriter s(SER_GETHASH, 0);
350 0 : s << nSporkID;
351 0 : s << nValue;
352 0 : s << nTimeSigned;
353 0 : return s.GetHash();
354 : }
355 :
356 696 : bool CSporkMessage::Sign(const CKey& key)
357 : {
358 696 : if (!key.IsValid()) {
359 0 : LogPrintf("CSporkMessage::Sign -- signing key is not valid\n");
360 0 : return false;
361 : }
362 :
363 696 : CKeyID pubKeyId = key.GetPubKey().GetID();
364 :
365 : // Harden Spork6 so that it is active on testnet and no other networks
366 1392 : if (std::string strError; Params().NetworkIDString() == CBaseChainParams::TESTNET) {
367 0 : uint256 hash = GetSignatureHash();
368 :
369 0 : if (!CHashSigner::SignHash(hash, key, vchSig)) {
370 0 : LogPrintf("CSporkMessage::Sign -- SignHash() failed\n");
371 0 : return false;
372 : }
373 :
374 0 : if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) {
375 0 : LogPrintf("CSporkMessage::Sign -- VerifyHash() failed, error: %s\n", strError);
376 0 : return false;
377 : }
378 0 : } else {
379 696 : std::string strMessage = ToString(nSporkID) + ToString(nValue) + ToString(nTimeSigned);
380 :
381 696 : if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) {
382 0 : LogPrintf("CSporkMessage::Sign -- SignMessage() failed\n");
383 0 : return false;
384 : }
385 :
386 696 : if (!CMessageSigner::VerifyMessage(pubKeyId, vchSig, strMessage, strError)) {
387 0 : LogPrintf("CSporkMessage::Sign -- VerifyMessage() failed, error: %s\n", strError);
388 0 : return false;
389 : }
390 696 : }
391 :
392 696 : return true;
393 696 : }
394 :
395 2283 : bool CSporkMessage::CheckSignature(const CKeyID& pubKeyId) const
396 : {
397 : // Harden Spork6 so that it is active on testnet and no other networks
398 4566 : if (std::string strError; Params().NetworkIDString() == CBaseChainParams::TESTNET) {
399 0 : uint256 hash = GetSignatureHash();
400 :
401 0 : if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) {
402 0 : LogPrint(BCLog::SPORK, "CSporkMessage::CheckSignature -- VerifyHash() failed, error: %s\n", strError);
403 0 : return false;
404 : }
405 0 : } else {
406 2283 : std::string strMessage = ToString(nSporkID) + ToString(nValue) + ToString(nTimeSigned);
407 :
408 2283 : if (!CMessageSigner::VerifyMessage(pubKeyId, vchSig, strMessage, strError)) {
409 3 : LogPrint(BCLog::SPORK, "CSporkMessage::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
410 3 : return false;
411 : }
412 2283 : }
413 :
414 2280 : return true;
415 2283 : }
416 :
417 2796 : std::optional<CKeyID> CSporkMessage::GetSignerKeyID() const
418 : {
419 2796 : CPubKey pubkeyFromSig;
420 : // Harden Spork6 so that it is active on testnet and no other networks
421 2796 : if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
422 0 : if (!pubkeyFromSig.RecoverCompact(GetSignatureHash(), vchSig)) {
423 0 : return std::nullopt;
424 : }
425 0 : } else {
426 2796 : std::string strMessage = ToString(nSporkID) + ToString(nValue) + ToString(nTimeSigned);
427 2796 : CHashWriter ss(SER_GETHASH, 0);
428 2796 : ss << MESSAGE_MAGIC;
429 2796 : ss << strMessage;
430 2796 : if (!pubkeyFromSig.RecoverCompact(ss.GetHash(), vchSig)) {
431 0 : return std::nullopt;
432 : }
433 2796 : }
434 :
435 2796 : return {pubkeyFromSig.GetID()};
436 2796 : }
|