PIVX Core  5.6.99
P2P Digital Currency
spork.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2016 The Dash developers
2 // Copyright (c) 2016-2022 The PIVX Core developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "spork.h"
7 
8 #include "netmessagemaker.h"
9 #include "sporkdb.h"
10 #include "validation.h"
11 
12 #include <iostream>
13 
14 #define MAKE_SPORK_DEF(name, defaultValue) CSporkDef(name, defaultValue, #name)
15 
16 std::vector<CSporkDef> sporkDefs = {
19  MAKE_SPORK_DEF(SPORK_13_ENABLE_SUPERBLOCKS, 4070908800ULL), // OFF
22  MAKE_SPORK_DEF(SPORK_19_COLDSTAKING_MAINTENANCE, 4070908800ULL), // OFF
23  MAKE_SPORK_DEF(SPORK_20_SAPLING_MAINTENANCE, 4070908800ULL), // OFF
24  MAKE_SPORK_DEF(SPORK_21_LEGACY_MNS_MAX_HEIGHT, 4070908800ULL), // OFF
25  MAKE_SPORK_DEF(SPORK_22_LLMQ_DKG_MAINTENANCE, 4070908800ULL), // OFF
26  MAKE_SPORK_DEF(SPORK_23_CHAINLOCKS_ENFORCEMENT, 4070908800ULL), // OFF
27 };
28 
30 std::map<uint256, CSporkMessage> mapSporks;
31 
33 {
34  for (auto& sporkDef : sporkDefs) {
35  sporkDefsById.emplace(sporkDef.sporkId, &sporkDef);
36  sporkDefsByName.emplace(sporkDef.name, &sporkDef);
37  }
38 }
39 
41 {
42  strMasterPrivKey = "";
43  mapSporksActive.clear();
44 }
45 
46 // PIVX: on startup load spork values from previous session if they exist in the sporkDB
48 {
49  for (const auto& sporkDef : sporkDefs) {
50  // attempt to read spork from sporkDB
52  if (!pSporkDB->ReadSpork(sporkDef.sporkId, spork)) {
53  LogPrintf("%s : no previous value for %s found in database\n", __func__, sporkDef.name);
54  continue;
55  }
56 
57  // TODO: Temporary workaround for v5.0 clients to ensure up-to-date protocol version spork
59  LogPrintf("%s : Spork 15 signed at %d\n", __func__, spork.nTimeSigned);
60  // 1578338986 is the timestamp that spork 15 was last signed at for mainnet for the previous
61  // protocol bump. If the timestamp in the DB is equal or lower than this, we know that
62  // the value is stale and should ignore it to prevent un-necessary disconnections in the
63  // version handshake process. This value is also suitable for testnet as the timestamp
64  // for this spork on that network was signed shortly after this.
65  if (spork.nTimeSigned <= 1578338986 ) {
66  LogPrintf("%s : Stale spork 15 detected, clearing...\n", __func__);
68  return;
69  }
70  }
71 
72  // add spork to memory
74 
75  std::time_t result = spork.nValue;
76  // If SPORK Value is greater than 1,000,000 assume it's actually a Date and then convert to a more readable format
77  std::string sporkName = sporkManager.GetSporkNameByID(spork.nSporkID);
78  if (spork.nValue > 1000000) {
79  char* res = std::ctime(&result);
80  LogPrintf("%s : loaded spork %s with value %d : %s\n", __func__, sporkName.c_str(), spork.nValue,
81  ((res) ? res : "no time") );
82  } else {
83  LogPrintf("%s : loaded spork %s with value %d\n", __func__,
84  sporkName, spork.nValue);
85  }
86  }
87 }
88 
89 bool CSporkManager::ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv, int& dosScore)
90 {
91  if (strCommand == NetMsgType::SPORK) {
92  dosScore = ProcessSporkMsg(vRecv);
93  return dosScore == 0;
94  }
95  if (strCommand == NetMsgType::GETSPORKS) {
96  ProcessGetSporks(pfrom, strCommand, vRecv);
97  }
98  return true;
99 }
100 
102 {
104  vRecv >> spork;
105  return ProcessSporkMsg(spork);
106 }
107 
109 {
110  // Ignore spork messages about unknown/deleted sporks
111  std::string strSpork = sporkManager.GetSporkNameByID(spork.nSporkID);
112  if (strSpork == "Unknown") return 0;
113 
114  // Do not accept sporks signed way too far into the future
115  if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) {
116  LogPrint(BCLog::SPORKS, "%s : ERROR: too far into the future\n", __func__);
117  return 100;
118  }
119 
120  // reject old signature version
121  if (spork.nMessVersion != MessageVersion::MESS_VER_HASH) {
122  LogPrint(BCLog::SPORKS, "%s : nMessVersion=%d not accepted anymore\n", __func__, spork.nMessVersion);
123  return 0;
124  }
125 
126  std::string sporkName = sporkManager.GetSporkNameByID(spork.nSporkID);
127  std::string strStatus;
128  {
129  LOCK(cs);
130  if (mapSporksActive.count(spork.nSporkID)) {
131  // spork is active
132  if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) {
133  // spork in memory has been signed more recently
134  LogPrint(BCLog::SPORKS, "%s : spork %d (%s) in memory is more recent: %d >= %d\n", __func__,
135  spork.nSporkID, sporkName,
136  mapSporksActive[spork.nSporkID].nTimeSigned, spork.nTimeSigned);
137  return 0;
138  } else {
139  // update active spork
140  strStatus = "updated";
141  }
142  } else {
143  // spork is not active
144  strStatus = "new";
145  }
146  }
147 
148  const bool fRequireNew = spork.nTimeSigned >= Params().GetConsensus().nTime_EnforceNewSporkKey;
149  bool fValidSig = spork.CheckSignature(spork.GetPublicKey().GetID());
150  if (!fValidSig && !fRequireNew) {
151  // See if window is open that allows for old spork key to sign messages
152  if (GetAdjustedTime() < Params().GetConsensus().nTime_RejectOldSporkKey) {
153  CPubKey pubkeyold = spork.GetPublicKeyOld();
154  fValidSig = spork.CheckSignature(pubkeyold.GetID());
155  }
156  }
157 
158  if (!fValidSig) {
159  LogPrint(BCLog::SPORKS, "%s : Invalid Signature\n", __func__);
160  return 100;
161  }
162 
163  // Log valid spork value change
164  LogPrintf("%s : got %s spork %d (%s) with value %d (signed at %d)\n", __func__,
165  strStatus, spork.nSporkID, sporkName, spork.nValue, spork.nTimeSigned);
166 
168  spork.Relay();
169 
170  // All good.
171  return 0;
172 }
173 
174 void CSporkManager::ProcessGetSporks(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
175 {
176  LOCK(cs);
177 
178  std::map<SporkId, CSporkMessage>::iterator it = mapSporksActive.begin();
179 
180  while (it != mapSporksActive.end()) {
181  g_connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, it->second));
182  it++;
183  }
184 
185  // end message
186  if (Params().IsRegTestNet()) {
187  // For now, only use it on regtest.
188  CSporkMessage msg(SPORK_INVALID, 0, 0);
189  g_connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, msg));
190  }
191 }
192 
193 bool CSporkManager::UpdateSpork(SporkId nSporkID, int64_t nValue)
194 {
195  CSporkMessage spork(nSporkID, nValue, GetTime());
196 
197  if (spork.Sign(strMasterPrivKey)) {
198  spork.Relay();
200  return true;
201  }
202 
203  return false;
204 }
205 
207 {
208  {
209  LOCK(cs);
210  mapSporks[spork.GetHash()] = spork;
211  mapSporksActive[spork.nSporkID] = spork;
212  }
213  if (flush) {
214  // add to spork database.
215  pSporkDB->WriteSpork(spork.nSporkID, spork);
216  }
217 }
218 
219 // grab the spork value, and see if it's off
221 {
222  return GetSporkValue(nSporkID) < GetAdjustedTime();
223 }
224 
225 // grab the value of the spork on the network, or the default
227 {
228  LOCK(cs);
229 
230  if (mapSporksActive.count(nSporkID)) {
231  return mapSporksActive[nSporkID].nValue;
232 
233  } else {
234  auto it = sporkDefsById.find(nSporkID);
235  if (it != sporkDefsById.end()) {
236  return it->second->defaultValue;
237  } else {
238  LogPrintf("%s : Unknown Spork %d\n", __func__, nSporkID);
239  }
240  }
241 
242  return -1;
243 }
244 
246 {
247  auto it = sporkDefsByName.find(strName);
248  if (it == sporkDefsByName.end()) {
249  LogPrintf("%s : Unknown Spork name '%s'\n", __func__, strName);
250  return SPORK_INVALID;
251  }
252  return it->second->sporkId;
253 }
254 
256 {
257  auto it = sporkDefsById.find(nSporkID);
258  if (it == sporkDefsById.end()) {
259  LogPrint(BCLog::SPORKS, "%s : Unknown Spork ID %d\n", __func__, nSporkID);
260  return "Unknown";
261  }
262  return it->second->name;
263 }
264 
265 bool CSporkManager::SetPrivKey(std::string strPrivKey)
266 {
268 
269  spork.Sign(strPrivKey);
270 
271  bool fValidSig = spork.CheckSignature(spork.GetPublicKey().GetID());
272  if (!fValidSig) {
273  // See if window is open that allows for old spork key to sign messages
274  if (GetAdjustedTime() < Params().GetConsensus().nTime_RejectOldSporkKey) {
275  CPubKey pubkeyold = spork.GetPublicKeyOld();
276  fValidSig = spork.CheckSignature(pubkeyold.GetID());
277  }
278  }
279  if (fValidSig) {
280  LOCK(cs);
281  // Test signing successful, proceed
282  LogPrintf("%s : Successfully initialized as spork signer\n", __func__);
283  strMasterPrivKey = strPrivKey;
284  return true;
285  }
286 
287  return false;
288 }
289 
290 std::string CSporkManager::ToString() const
291 {
292  LOCK(cs);
293  return strprintf("Sporks: %llu", mapSporksActive.size());
294 }
295 
297 {
298  CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
299  ss << nMessVersion;
300  ss << nSporkID;
301  ss << nValue;
302  ss << nTimeSigned;
303  return ss.GetHash();
304 }
305 
306 std::string CSporkMessage::GetStrMessage() const
307 {
308  return std::to_string(nSporkID) +
309  std::to_string(nValue) +
310  std::to_string(nTimeSigned);
311 }
312 
314 {
315  return CPubKey(ParseHex(Params().GetConsensus().strSporkPubKey));
316 }
317 
319 {
320  return CPubKey(ParseHex(Params().GetConsensus().strSporkPubKeyOld));
321 }
322 
324 {
325  CInv inv(MSG_SPORK, GetHash());
326  g_connman->RelayInv(inv);
327 }
328 
const CChainParams & Params()
Return the currently selected parameters.
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:216
uint256 GetHash()
Definition: hash.h:236
inv message data
Definition: protocol.h:466
CSerializedNetMsg Make(int nFlags, std::string sCommand, Args &&... args)
Information about a peer.
Definition: net.h:669
int GetSendVersion() const
Definition: net.cpp:789
An encapsulated public key.
Definition: pubkey.h:44
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:167
void ProcessGetSporks(CNode *pfrom, std::string &strCommand, CDataStream &vRecv)
Definition: spork.cpp:174
void LoadSporksFromDB()
Definition: spork.cpp:47
std::string strMasterPrivKey
Definition: spork.h:74
std::map< SporkId, CSporkMessage > mapSporksActive
Definition: spork.h:77
bool UpdateSpork(SporkId nSporkID, int64_t nValue)
Definition: spork.cpp:193
std::map< std::string, CSporkDef * > sporkDefsByName
Definition: spork.h:76
bool SetPrivKey(std::string strPrivKey)
Definition: spork.cpp:265
SporkId GetSporkIDByName(std::string strName)
Definition: spork.cpp:245
std::string GetSporkNameByID(SporkId id)
Definition: spork.cpp:255
std::map< SporkId, CSporkDef * > sporkDefsById
Definition: spork.h:75
std::string ToString() const
Definition: spork.cpp:290
int64_t GetSporkValue(SporkId nSporkID)
Definition: spork.cpp:226
bool ProcessSpork(CNode *pfrom, std::string &strCommand, CDataStream &vRecv, int &dosScore)
Definition: spork.cpp:89
RecursiveMutex cs
Definition: spork.h:73
int ProcessSporkMsg(CDataStream &vRecv)
Definition: spork.cpp:101
void Clear()
Definition: spork.cpp:40
bool IsSporkActive(SporkId nSporkID)
Definition: spork.cpp:220
void AddOrUpdateSporkMessage(const CSporkMessage &spork, bool flush=false)
Definition: spork.cpp:206
CSporkManager()
Definition: spork.cpp:32
uint256 GetSignatureHash() const override
Definition: spork.cpp:296
const CPubKey GetPublicKey() const
Definition: spork.cpp:313
const CPubKey GetPublicKeyOld() const
Definition: spork.cpp:318
uint256 GetHash() const
Definition: spork.h:54
int64_t nTimeSigned
Definition: spork.h:38
SporkId nSporkID
Definition: spork.h:36
std::string GetStrMessage() const override
Definition: spork.cpp:306
void Relay()
Definition: spork.cpp:323
int64_t nValue
Definition: spork.h:37
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
@ LOCK
Definition: lockunlock.h:16
#define LogPrint(category,...)
Definition: logging.h:163
@ MESS_VER_HASH
Definition: messagesigner.h:17
UniValue spork(const JSONRPCRequest &request)
Definition: misc.cpp:286
@ SPORKS
Definition: logging.h:64
const char * SPORK
The spork message is used to send spork values to connected peers.
Definition: protocol.cpp:42
const char * GETSPORKS
The getsporks message is used to request spork data from connected peers.
Definition: protocol.cpp:43
@ MSG_SPORK
Definition: protocol.h:443
@ SER_GETHASH
Definition: serialize.h:176
#define MAKE_SPORK_DEF(name, defaultValue)
Definition: spork.cpp:14
std::vector< CSporkDef > sporkDefs
Definition: spork.cpp:16
std::map< uint256, CSporkMessage > mapSporks
Definition: spork.cpp:30
CSporkManager sporkManager
Definition: spork.cpp:29
SporkId
Definition: sporkid.h:14
@ SPORK_23_CHAINLOCKS_ENFORCEMENT
Definition: sporkid.h:30
@ SPORK_22_LLMQ_DKG_MAINTENANCE
Definition: sporkid.h:29
@ SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2
Definition: sporkid.h:22
@ SPORK_INVALID
Definition: sporkid.h:32
@ SPORK_14_NEW_PROTOCOL_ENFORCEMENT
Definition: sporkid.h:21
@ SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT
Definition: sporkid.h:19
@ SPORK_13_ENABLE_SUPERBLOCKS
Definition: sporkid.h:20
@ SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT
Definition: sporkid.h:18
@ SPORK_19_COLDSTAKING_MAINTENANCE
Definition: sporkid.h:26
@ SPORK_20_SAPLING_MAINTENANCE
Definition: sporkid.h:27
@ SPORK_21_LEGACY_MNS_MAX_HEIGHT
Definition: sporkid.h:28
int64_t nTime_EnforceNewSporkKey
Definition: params.h:200
int64_t GetAdjustedTime()
Definition: timedata.cpp:36
#define strprintf
Definition: tinyformat.h:1056
std::vector< unsigned char > ParseHex(const char *psz)
int64_t GetTime()
DEPRECATED Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
Definition: utiltime.cpp:27
std::unique_ptr< CSporkDB > pSporkDB
Global variable that points to the spork database (protected by cs_main)
Definition: validation.cpp:209