1 // Copyright (c) 2009-2012 The Bitcoin developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include <boost/assign/list_of.hpp> // for 'map_list_of()'
6 #include <boost/foreach.hpp>
8 #include "checkpoints.h"
16 typedef std::map<int, std::pair<uint256, unsigned int> > MapCheckpoints;
19 // What makes a good checkpoint block?
20 // + Is surrounded by blocks with reasonable timestamps
21 // (no blocks before with a timestamp after, none after with
23 // + Contains no strange transactions
25 static MapCheckpoints mapCheckpoints =
26 boost::assign::map_list_of
27 ( 0, std::make_pair(hashGenesisBlock, 1360105017) )
28 ( 13560, std::make_pair(uint256("0xa1591a0fcbf11f282d671581edb9f0aadcd06fee69761081e0a3245914c13729"), 1364674052) )
29 ( 143990, std::make_pair(uint256("0x00000000001ff5c3940a9f73ad4a990f64955179bde0f743c76dbf0031429efc"), 1418953493) )
30 ( 149000, std::make_pair(uint256("0x7a24acfcadcf43054e7f7d9f273522c0dfc5791ba4006e0273e7521a8d36c525"), 1420872125) )
31 ( 160000, std::make_pair(uint256("0x000000000001cb1133043d38d077c0e93f66c8b2566779f10f182137d1e34a68"), 1425150237) )
34 // TestNet has no checkpoints
35 static MapCheckpoints mapCheckpointsTestnet =
36 boost::assign::map_list_of
37 ( 0, std::make_pair(hashGenesisBlockTestNet, 1360105017) )
40 bool CheckHardened(int nHeight, const uint256& hash)
42 MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
44 MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
45 if (i == checkpoints.end()) return true;
46 return hash == i->second.first;
49 int GetTotalBlocksEstimate()
51 MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
53 return checkpoints.rbegin()->first;
56 unsigned int GetLastCheckpointTime()
58 MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
60 return checkpoints.rbegin()->second.second;
63 CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
65 MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
67 BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
69 const uint256& hash = i.second.first;
70 std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
71 if (t != mapBlockIndex.end())
77 // ppcoin: synchronized checkpoint (centrally broadcasted)
78 uint256 hashSyncCheckpoint = 0;
79 uint256 hashPendingCheckpoint = 0;
80 CSyncCheckpoint checkpointMessage;
81 CSyncCheckpoint checkpointMessagePending;
82 uint256 hashInvalidCheckpoint = 0;
83 CCriticalSection cs_hashSyncCheckpoint;
85 // ppcoin: get last synchronized checkpoint
86 CBlockIndex* GetLastSyncCheckpoint()
88 LOCK(cs_hashSyncCheckpoint);
89 if (!mapBlockIndex.count(hashSyncCheckpoint))
90 error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
92 return mapBlockIndex[hashSyncCheckpoint];
96 // ppcoin: only descendant of current sync-checkpoint is allowed
97 bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
99 if (!mapBlockIndex.count(hashSyncCheckpoint))
100 return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
101 if (!mapBlockIndex.count(hashCheckpoint))
102 return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str());
104 CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
105 CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
107 if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
109 // Received an older checkpoint, trace back from current checkpoint
110 // to the same height of the received checkpoint to verify
111 // that current checkpoint should be a descendant block
112 CBlockIndex* pindex = pindexSyncCheckpoint;
113 while (pindex->nHeight > pindexCheckpointRecv->nHeight)
114 if (!(pindex = pindex->pprev))
115 return error("ValidateSyncCheckpoint: pprev null - block index structure failure");
116 if (pindex->GetBlockHash() != hashCheckpoint)
118 hashInvalidCheckpoint = hashCheckpoint;
119 return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
121 return false; // ignore older checkpoint
124 // Received checkpoint should be a descendant block of the current
125 // checkpoint. Trace back to the same height of current checkpoint
127 CBlockIndex* pindex = pindexCheckpointRecv;
128 while (pindex->nHeight > pindexSyncCheckpoint->nHeight)
129 if (!(pindex = pindex->pprev))
130 return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure");
131 if (pindex->GetBlockHash() != hashSyncCheckpoint)
133 hashInvalidCheckpoint = hashCheckpoint;
134 return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
139 bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
143 if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
146 return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
148 if (!txdb.TxnCommit())
149 return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
155 Checkpoints::hashSyncCheckpoint = hashCheckpoint;
159 bool AcceptPendingSyncCheckpoint()
161 LOCK(cs_hashSyncCheckpoint);
162 if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
164 if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
166 hashPendingCheckpoint = 0;
167 checkpointMessagePending.SetNull();
172 CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
173 if (!pindexCheckpoint->IsInMainChain())
176 if (!block.ReadFromDisk(pindexCheckpoint))
177 return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
178 if (!block.SetBestChain(txdb, pindexCheckpoint))
180 hashInvalidCheckpoint = hashPendingCheckpoint;
181 return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
188 if (!WriteSyncCheckpoint(hashPendingCheckpoint))
189 return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
190 hashPendingCheckpoint = 0;
191 checkpointMessage = checkpointMessagePending;
192 checkpointMessagePending.SetNull();
193 printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str());
194 // relay the checkpoint
195 if (!checkpointMessage.IsNull())
197 BOOST_FOREACH(CNode* pnode, vNodes)
198 checkpointMessage.RelayTo(pnode);
205 // Automatically select a suitable sync-checkpoint
206 uint256 AutoSelectSyncCheckpoint()
208 const CBlockIndex *pindex = pindexBest;
209 // Search backward for a block within max span and maturity window
210 while (pindex->pprev && (pindex->GetBlockTime() + CHECKPOINT_MAX_SPAN > pindexBest->GetBlockTime() || pindex->nHeight + 8 > pindexBest->nHeight))
211 pindex = pindex->pprev;
212 return pindex->GetBlockHash();
215 // Check against synchronized checkpoint
216 bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
218 if (fTestNet) return true; // Testnet has no checkpoints
219 int nHeight = pindexPrev->nHeight + 1;
221 LOCK(cs_hashSyncCheckpoint);
222 // sync-checkpoint should always be accepted block
223 assert(mapBlockIndex.count(hashSyncCheckpoint));
224 const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
226 if (nHeight > pindexSync->nHeight)
228 // trace back to same height as sync-checkpoint
229 const CBlockIndex* pindex = pindexPrev;
230 while (pindex->nHeight > pindexSync->nHeight)
231 if (!(pindex = pindex->pprev))
232 return error("CheckSync: pprev null - block index structure failure");
233 if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
234 return false; // only descendant of sync-checkpoint can pass check
236 if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
237 return false; // same height with sync-checkpoint
238 if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
239 return false; // lower height than sync-checkpoint
243 bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
245 LOCK(cs_hashSyncCheckpoint);
246 if (hashPendingCheckpoint == 0)
248 if (hashBlock == hashPendingCheckpoint)
250 if (mapOrphanBlocks.count(hashPendingCheckpoint)
251 && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
256 // ppcoin: reset synchronized checkpoint to last hardened checkpoint
257 bool ResetSyncCheckpoint()
259 LOCK(cs_hashSyncCheckpoint);
260 const uint256& hash = mapCheckpoints.rbegin()->second.first;
261 if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
263 // checkpoint block accepted but not yet in main chain
264 printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str());
267 if (!block.ReadFromDisk(mapBlockIndex[hash]))
268 return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str());
269 if (!block.SetBestChain(txdb, mapBlockIndex[hash]))
271 return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str());
279 else if(!mapBlockIndex.count(hash))
281 // checkpoint block not yet accepted
282 hashPendingCheckpoint = hash;
283 checkpointMessagePending.SetNull();
284 printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str());
287 BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
289 const uint256& hash = i.second.first;
290 if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
292 if (!WriteSyncCheckpoint(hash))
293 return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str());
294 printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str());
302 void AskForPendingSyncCheckpoint(CNode* pfrom)
304 LOCK(cs_hashSyncCheckpoint);
305 if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
306 pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
309 bool SetCheckpointPrivKey(std::string strPrivKey)
311 // Test signing a sync-checkpoint with genesis block
312 CSyncCheckpoint checkpoint;
313 checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet;
314 CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
315 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
316 checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
318 std::vector<unsigned char> vchPrivKey = ParseHex(strPrivKey);
320 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
321 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
324 // Test signing successful, proceed
325 CSyncCheckpoint::strMasterPrivKey = strPrivKey;
329 bool SendSyncCheckpoint(uint256 hashCheckpoint)
331 CSyncCheckpoint checkpoint;
332 checkpoint.hashCheckpoint = hashCheckpoint;
333 CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
334 sMsg << (CUnsignedSyncCheckpoint)checkpoint;
335 checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
337 if (CSyncCheckpoint::strMasterPrivKey.empty())
338 return error("SendSyncCheckpoint: Checkpoint master key unavailable.");
339 std::vector<unsigned char> vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey);
341 key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
342 if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
343 return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?");
345 if(!checkpoint.ProcessSyncCheckpoint(NULL))
347 printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n");
354 BOOST_FOREACH(CNode* pnode, vNodes)
355 checkpoint.RelayTo(pnode);
360 // Is the sync-checkpoint outside maturity window?
361 bool IsMatureSyncCheckpoint()
363 LOCK(cs_hashSyncCheckpoint);
364 // sync-checkpoint should always be accepted block
365 assert(mapBlockIndex.count(hashSyncCheckpoint));
366 const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
367 return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity ||
368 pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime());
372 // ppcoin: sync-checkpoint master key
373 const std::string CSyncCheckpoint::strMasterPubKey = "04a51b735f816de4ec3f891d5b38bbc91e1f7245c7c08d17990760b86b4d8fc3910a850ffecf73bfa8886f01739a0c4c4322201282d07b6e48ce931cc92af94850";
375 std::string CSyncCheckpoint::strMasterPrivKey = "";
377 // ppcoin: verify signature of sync-checkpoint message
378 bool CSyncCheckpoint::CheckSignature()
381 if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey)))
382 return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed");
383 if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
384 return error("CSyncCheckpoint::CheckSignature() : verify signature failed");
386 // Now unserialize the data
387 CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
388 sMsg >> *(CUnsignedSyncCheckpoint*)this;
392 // process synchronized checkpoint
393 bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
395 if (!CheckSignature())
398 LOCK(Checkpoints::cs_hashSyncCheckpoint);
399 if (!mapBlockIndex.count(hashCheckpoint))
401 // We haven't received the checkpoint chain, keep the checkpoint as pending
402 Checkpoints::hashPendingCheckpoint = hashCheckpoint;
403 Checkpoints::checkpointMessagePending = *this;
404 printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str());
405 // Ask this guy to fill in what we're missing
408 pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
409 // ask directly as well in case rejected earlier by duplicate
410 // proof-of-stake because getblocks may not get it this time
411 pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
416 if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
420 CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
421 if (!pindexCheckpoint->IsInMainChain())
423 // checkpoint chain received but not yet main chain
425 if (!block.ReadFromDisk(pindexCheckpoint))
426 return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
427 if (!block.SetBestChain(txdb, pindexCheckpoint))
429 Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
430 return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
438 if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint))
439 return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str());
440 Checkpoints::checkpointMessage = *this;
441 Checkpoints::hashPendingCheckpoint = 0;
442 Checkpoints::checkpointMessagePending.SetNull();
443 printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str());