checkpoints.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. // ECOin - Copyright (c) - 2014/2022 - GPLv3 - epsylon@riseup.net (https://03c8.net)
  2. #include <boost/assign/list_of.hpp> // for 'map_list_of()'
  3. #include <boost/foreach.hpp>
  4. #include "checkpoints.h"
  5. #include "txdb.h"
  6. #include "main.h"
  7. #include "uint256.h"
  8. namespace Checkpoints
  9. {
  10. typedef std::map<int, std::pair<uint256, unsigned int> > MapCheckpoints;
  11. static MapCheckpoints mapCheckpoints =
  12. boost::assign::map_list_of
  13. ( 0, std::make_pair(hashGenesisBlock, 1610833657) )
  14. ;
  15. static MapCheckpoints mapCheckpointsTestnet =
  16. boost::assign::map_list_of
  17. ( 0, std::make_pair(hashGenesisBlockTestNet, 1610833657) )
  18. ;
  19. bool CheckHardened(int nHeight, const uint256& hash)
  20. {
  21. MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  22. MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
  23. if (i == checkpoints.end()) return true;
  24. return hash == i->second.first;
  25. }
  26. int GetTotalBlocksEstimate()
  27. {
  28. MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  29. return checkpoints.rbegin()->first;
  30. }
  31. int GetLastCheckpointTime()
  32. {
  33. MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  34. return checkpoints.rbegin()->second.second;
  35. }
  36. CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
  37. {
  38. MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  39. BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
  40. {
  41. const uint256& hash = i.second.first;
  42. std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
  43. if (t != mapBlockIndex.end())
  44. return t->second;
  45. }
  46. return NULL;
  47. }
  48. // ecoin: synchronized checkpoint (centrally broadcasted)
  49. uint256 hashSyncCheckpoint = 0;
  50. uint256 hashPendingCheckpoint = 0;
  51. CSyncCheckpoint checkpointMessage;
  52. CSyncCheckpoint checkpointMessagePending;
  53. uint256 hashInvalidCheckpoint = 0;
  54. CCriticalSection cs_hashSyncCheckpoint;
  55. // ecoin: get last synchronized checkpoint
  56. CBlockIndex* GetLastSyncCheckpoint()
  57. {
  58. LOCK(cs_hashSyncCheckpoint);
  59. if (!mapBlockIndex.count(hashSyncCheckpoint))
  60. error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
  61. else
  62. return mapBlockIndex[hashSyncCheckpoint];
  63. return NULL;
  64. }
  65. // ecoin: only descendant of current sync-checkpoint is allowed
  66. bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
  67. {
  68. if (!mapBlockIndex.count(hashSyncCheckpoint))
  69. return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
  70. if (!mapBlockIndex.count(hashCheckpoint))
  71. return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str());
  72. CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
  73. CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
  74. if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
  75. {
  76. CBlockIndex* pindex = pindexSyncCheckpoint;
  77. while (pindex->nHeight > pindexCheckpointRecv->nHeight)
  78. if (!(pindex = pindex->pprev))
  79. return error("ValidateSyncCheckpoint: pprev null - block index structure failure");
  80. if (pindex->GetBlockHash() != hashCheckpoint)
  81. {
  82. hashInvalidCheckpoint = hashCheckpoint;
  83. return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
  84. }
  85. return false; // ignore older checkpoint
  86. }
  87. CBlockIndex* pindex = pindexCheckpointRecv;
  88. while (pindex->nHeight > pindexSyncCheckpoint->nHeight)
  89. if (!(pindex = pindex->pprev))
  90. return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure");
  91. if (pindex->GetBlockHash() != hashSyncCheckpoint)
  92. {
  93. hashInvalidCheckpoint = hashCheckpoint;
  94. return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
  95. }
  96. return true;
  97. }
  98. bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
  99. {
  100. CTxDB txdb;
  101. txdb.TxnBegin();
  102. if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
  103. {
  104. txdb.TxnAbort();
  105. return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
  106. }
  107. if (!txdb.TxnCommit())
  108. return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
  109. Checkpoints::hashSyncCheckpoint = hashCheckpoint;
  110. return true;
  111. }
  112. bool AcceptPendingSyncCheckpoint()
  113. {
  114. LOCK(cs_hashSyncCheckpoint);
  115. if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
  116. {
  117. if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
  118. {
  119. hashPendingCheckpoint = 0;
  120. checkpointMessagePending.SetNull();
  121. return false;
  122. }
  123. CTxDB txdb;
  124. CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
  125. if (!pindexCheckpoint->IsInMainChain())
  126. {
  127. CBlock block;
  128. if (!block.ReadFromDisk(pindexCheckpoint))
  129. return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
  130. if (!block.SetBestChain(txdb, pindexCheckpoint))
  131. {
  132. hashInvalidCheckpoint = hashPendingCheckpoint;
  133. return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
  134. }
  135. }
  136. if (!WriteSyncCheckpoint(hashPendingCheckpoint))
  137. return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
  138. hashPendingCheckpoint = 0;
  139. checkpointMessage = checkpointMessagePending;
  140. checkpointMessagePending.SetNull();
  141. printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str());
  142. // relay the checkpoint
  143. if (!checkpointMessage.IsNull())
  144. {
  145. BOOST_FOREACH(CNode* pnode, vNodes)
  146. checkpointMessage.RelayTo(pnode);
  147. }
  148. return true;
  149. }
  150. return false;
  151. }
  152. // Automatically select a suitable sync-checkpoint
  153. uint256 AutoSelectSyncCheckpoint()
  154. {
  155. const CBlockIndex *pindex = pindexBest;
  156. // Search backward for a block within max span and maturity window
  157. while (pindex->pprev && (pindex->GetBlockTime() + CHECKPOINT_MAX_SPAN > pindexBest->GetBlockTime() || pindex->nHeight + 8 > pindexBest->nHeight))
  158. pindex = pindex->pprev;
  159. return pindex->GetBlockHash();
  160. }
  161. // Check against synchronized checkpoint
  162. bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
  163. {
  164. if (fTestNet) return true; // Testnet has no checkpoints
  165. int nHeight = pindexPrev->nHeight + 1;
  166. LOCK(cs_hashSyncCheckpoint);
  167. // sync-checkpoint should always be accepted block
  168. assert(mapBlockIndex.count(hashSyncCheckpoint));
  169. const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
  170. if (nHeight > pindexSync->nHeight)
  171. {
  172. // trace back to same height as sync-checkpoint
  173. const CBlockIndex* pindex = pindexPrev;
  174. while (pindex->nHeight > pindexSync->nHeight)
  175. if (!(pindex = pindex->pprev))
  176. return error("CheckSync: pprev null - block index structure failure");
  177. if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
  178. return false; // only descendant of sync-checkpoint can pass check
  179. }
  180. if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
  181. return false; // same height with sync-checkpoint
  182. if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
  183. return false; // lower height than sync-checkpoint
  184. return true;
  185. }
  186. bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
  187. {
  188. LOCK(cs_hashSyncCheckpoint);
  189. if (hashPendingCheckpoint == 0)
  190. return false;
  191. if (hashBlock == hashPendingCheckpoint)
  192. return true;
  193. if (mapOrphanBlocks.count(hashPendingCheckpoint)
  194. && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
  195. return true;
  196. return false;
  197. }
  198. // ecoin: reset synchronized checkpoint to last hardened checkpoint
  199. bool ResetSyncCheckpoint()
  200. {
  201. LOCK(cs_hashSyncCheckpoint);
  202. const uint256& hash = mapCheckpoints.rbegin()->second.first;
  203. if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
  204. {
  205. // checkpoint block accepted but not yet in main chain
  206. printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str());
  207. CTxDB txdb;
  208. CBlock block;
  209. if (!block.ReadFromDisk(mapBlockIndex[hash]))
  210. return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str());
  211. if (!block.SetBestChain(txdb, mapBlockIndex[hash]))
  212. {
  213. return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str());
  214. }
  215. }
  216. else if(!mapBlockIndex.count(hash))
  217. {
  218. // checkpoint block not yet accepted
  219. hashPendingCheckpoint = hash;
  220. checkpointMessagePending.SetNull();
  221. printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str());
  222. }
  223. BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
  224. {
  225. const uint256& hash = i.second.first;
  226. if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
  227. {
  228. if (!WriteSyncCheckpoint(hash))
  229. return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str());
  230. printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str());
  231. return true;
  232. }
  233. }
  234. return false;
  235. }
  236. void AskForPendingSyncCheckpoint(CNode* pfrom)
  237. {
  238. LOCK(cs_hashSyncCheckpoint);
  239. if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
  240. pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
  241. }
  242. bool SetCheckpointPrivKey(std::string strPrivKey)
  243. {
  244. // Test signing a sync-checkpoint with genesis block
  245. CSyncCheckpoint checkpoint;
  246. checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet;
  247. CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
  248. sMsg << (CUnsignedSyncCheckpoint)checkpoint;
  249. checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
  250. std::vector<unsigned char> vchPrivKey = ParseHex(strPrivKey);
  251. CKey key;
  252. key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
  253. if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
  254. return false;
  255. // Test signing successful, proceed
  256. CSyncCheckpoint::strMasterPrivKey = strPrivKey;
  257. return true;
  258. }
  259. bool SendSyncCheckpoint(uint256 hashCheckpoint)
  260. {
  261. CSyncCheckpoint checkpoint;
  262. checkpoint.hashCheckpoint = hashCheckpoint;
  263. CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
  264. sMsg << (CUnsignedSyncCheckpoint)checkpoint;
  265. checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
  266. if (CSyncCheckpoint::strMasterPrivKey.empty())
  267. return error("SendSyncCheckpoint: Checkpoint master key unavailable.");
  268. std::vector<unsigned char> vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey);
  269. CKey key;
  270. key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
  271. if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
  272. return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?");
  273. if(!checkpoint.ProcessSyncCheckpoint(NULL))
  274. {
  275. printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n");
  276. return false;
  277. }
  278. // Relay checkpoint
  279. {
  280. LOCK(cs_vNodes);
  281. BOOST_FOREACH(CNode* pnode, vNodes)
  282. checkpoint.RelayTo(pnode);
  283. }
  284. return true;
  285. }
  286. // Is the sync-checkpoint outside maturity window?
  287. bool IsMatureSyncCheckpoint()
  288. {
  289. LOCK(cs_hashSyncCheckpoint);
  290. // sync-checkpoint should always be accepted block
  291. assert(mapBlockIndex.count(hashSyncCheckpoint));
  292. const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
  293. return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity ||
  294. pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime());
  295. }
  296. }
  297. // ecoin: sync-checkpoint master key
  298. const std::string CSyncCheckpoint::strMasterPubKey = "045d0773e7224ee40c6e69755c0343022b8f125f218a5ec5f6e57b31c00e182e048807d7e66548148795c1dfa4c972b6792ad41efea33111c5fc0d6c86fc0383f1";
  299. std::string CSyncCheckpoint::strMasterPrivKey = "";
  300. // ecoin: verify signature of sync-checkpoint message
  301. bool CSyncCheckpoint::CheckSignature()
  302. {
  303. CKey key;
  304. if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey)))
  305. return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed");
  306. if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
  307. return error("CSyncCheckpoint::CheckSignature() : verify signature failed");
  308. // Now unserialize the data
  309. CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
  310. sMsg >> *(CUnsignedSyncCheckpoint*)this;
  311. return true;
  312. }
  313. // ecoin: process synchronized checkpoint
  314. bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
  315. {
  316. if (!CheckSignature())
  317. return false;
  318. LOCK(Checkpoints::cs_hashSyncCheckpoint);
  319. if (!mapBlockIndex.count(hashCheckpoint))
  320. {
  321. // We haven't received the checkpoint chain, keep the checkpoint as pending
  322. Checkpoints::hashPendingCheckpoint = hashCheckpoint;
  323. Checkpoints::checkpointMessagePending = *this;
  324. printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str());
  325. // Ask this guy to fill in what we're missing
  326. if (pfrom)
  327. {
  328. pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
  329. // ask directly as well in case rejected earlier by duplicate
  330. // proof-of-stake because getblocks may not get it this time
  331. pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
  332. }
  333. return false;
  334. }
  335. if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
  336. return false;
  337. CTxDB txdb;
  338. CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
  339. if (!pindexCheckpoint->IsInMainChain())
  340. {
  341. // checkpoint chain received but not yet main chain
  342. CBlock block;
  343. if (!block.ReadFromDisk(pindexCheckpoint))
  344. return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
  345. if (!block.SetBestChain(txdb, pindexCheckpoint))
  346. {
  347. Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
  348. return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
  349. }
  350. }
  351. if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint))
  352. return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str());
  353. Checkpoints::checkpointMessage = *this;
  354. Checkpoints::hashPendingCheckpoint = 0;
  355. Checkpoints::checkpointMessagePending.SetNull();
  356. printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str());
  357. return true;
  358. }