Add checkpoint at block #24451
[novacoin.git] / src / checkpoints.cpp
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.
4
5 #include <boost/assign/list_of.hpp> // for 'map_list_of()'
6 #include <boost/foreach.hpp>
7
8 #include "checkpoints.h"
9
10 #include "db.h"
11 #include "main.h"
12 #include "uint256.h"
13
14 namespace Checkpoints
15 {
16     typedef std::map<int, uint256> MapCheckpoints;
17
18     //
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
22     //    timestamp before)
23     // + Contains no strange transactions
24     //
25     static MapCheckpoints mapCheckpoints =
26         boost::assign::map_list_of
27         ( 0, hashGenesisBlock )
28         ( 6000, uint256("0x000000000945e3c9d8e15df834e802521eb79f9ceb4191a27bdfadad4b777f4a"))
29         ( 9690, uint256("0x00000000026561450859c46868099e0df6068a538f038cb18988fd8d47dcdaf5"))
30         ( 13560, uint256("0xa1591a0fcbf11f282d671581edb9f0aadcd06fee69761081e0a3245914c13729"))
31         ( 14189, uint256("0x00000000020f76474d2522b19c7bfafc43ba6ecbabae54293bcd9546159c8c1d"))
32         ( 19600, uint256("0x252ae08d6df4bc7220c1dcdaed7b8a6e78bab05a60173511e8f565a3a38ce3c3"))
33         ( 21800, uint256("0x64bdeb35023f240ed01beaa082f840f2d4cc6defccbbcda1260de87e58296b37"))
34         ( 26174, uint256("0x00000000006cf1bfaa0d73caea92f6d2afa465651a76415b8d53b0bb0e01a8be"))
35         ( 27451, uint256("0xd65424c24041b9f2e531f8f275f1a3c8bb105f0b29005b0bd577ae1a73341080"))
36         
37         ;
38
39     // TestNet has no checkpoints
40     static MapCheckpoints mapCheckpointsTestnet =
41         boost::assign::map_list_of
42         ( 0, hashGenesisBlockTestNet )
43         ;
44
45     bool CheckHardened(int nHeight, const uint256& hash)
46     {
47         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
48
49         MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
50         if (i == checkpoints.end()) return true;
51         return hash == i->second;
52     }
53
54     int GetTotalBlocksEstimate()
55     {
56         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
57
58         return checkpoints.rbegin()->first;
59     }
60
61     CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
62     {
63         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
64
65         BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
66         {
67             const uint256& hash = i.second;
68             std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
69             if (t != mapBlockIndex.end())
70                 return t->second;
71         }
72         return NULL;
73     }
74
75     // ppcoin: synchronized checkpoint (centrally broadcasted)
76     uint256 hashSyncCheckpoint = 0;
77     uint256 hashPendingCheckpoint = 0;
78     CSyncCheckpoint checkpointMessage;
79     CSyncCheckpoint checkpointMessagePending;
80     uint256 hashInvalidCheckpoint = 0;
81     CCriticalSection cs_hashSyncCheckpoint;
82
83     // ppcoin: get last synchronized checkpoint
84     CBlockIndex* GetLastSyncCheckpoint()
85     {
86         LOCK(cs_hashSyncCheckpoint);
87         if (!mapBlockIndex.count(hashSyncCheckpoint))
88             error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
89         else
90             return mapBlockIndex[hashSyncCheckpoint];
91         return NULL;
92     }
93
94     // ppcoin: only descendant of current sync-checkpoint is allowed
95     bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
96     {
97         if (!mapBlockIndex.count(hashSyncCheckpoint))
98             return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
99         if (!mapBlockIndex.count(hashCheckpoint))
100             return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str());
101
102         CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
103         CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
104
105         if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
106         {
107             // Received an older checkpoint, trace back from current checkpoint
108             // to the same height of the received checkpoint to verify
109             // that current checkpoint should be a descendant block
110             CBlockIndex* pindex = pindexSyncCheckpoint;
111             while (pindex->nHeight > pindexCheckpointRecv->nHeight)
112                 if (!(pindex = pindex->pprev))
113                     return error("ValidateSyncCheckpoint: pprev1 null - block index structure failure");
114             if (pindex->GetBlockHash() != hashCheckpoint)
115             {
116                 hashInvalidCheckpoint = hashCheckpoint;
117                 return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
118             }
119             return false; // ignore older checkpoint
120         }
121
122         // Received checkpoint should be a descendant block of the current
123         // checkpoint. Trace back to the same height of current checkpoint
124         // to verify.
125         CBlockIndex* pindex = pindexCheckpointRecv;
126         while (pindex->nHeight > pindexSyncCheckpoint->nHeight)
127             if (!(pindex = pindex->pprev))
128                 return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure");
129         if (pindex->GetBlockHash() != hashSyncCheckpoint)
130         {
131             hashInvalidCheckpoint = hashCheckpoint;
132             return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
133         }
134         return true;
135     }
136
137     bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
138     {
139         CTxDB txdb;
140         txdb.TxnBegin();
141         if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
142         {
143             txdb.TxnAbort();
144             return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
145         }
146         if (!txdb.TxnCommit())
147             return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
148         txdb.Close();
149
150         Checkpoints::hashSyncCheckpoint = hashCheckpoint;
151         return true;
152     }
153
154     bool AcceptPendingSyncCheckpoint()
155     {
156         LOCK(cs_hashSyncCheckpoint);
157         if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
158         {
159             if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
160             {
161                 hashPendingCheckpoint = 0;
162                 checkpointMessagePending.SetNull();
163                 return false;
164             }
165
166             CTxDB txdb;
167             CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
168             if (!pindexCheckpoint->IsInMainChain())
169             {
170                 CBlock block;
171                 if (!block.ReadFromDisk(pindexCheckpoint))
172                     return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
173                 if (!block.SetBestChain(txdb, pindexCheckpoint))
174                 {
175                     hashInvalidCheckpoint = hashPendingCheckpoint;
176                     return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
177                 }
178             }
179             txdb.Close();
180
181             if (!WriteSyncCheckpoint(hashPendingCheckpoint))
182                 return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
183             hashPendingCheckpoint = 0;
184             checkpointMessage = checkpointMessagePending;
185             checkpointMessagePending.SetNull();
186             printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str());
187             // relay the checkpoint
188             if (!checkpointMessage.IsNull())
189             {
190                 BOOST_FOREACH(CNode* pnode, vNodes)
191                     checkpointMessage.RelayTo(pnode);
192             }
193             return true;
194         }
195         return false;
196     }
197
198     // Automatically select a suitable sync-checkpoint 
199     uint256 AutoSelectSyncCheckpoint()
200     {
201         // Proof-of-work blocks are immediately checkpointed
202         // to defend against 51% attack which rejects other miners block 
203
204         // Select the last proof-of-work block
205         const CBlockIndex *pindex = GetLastBlockIndex(pindexBest, false);
206         // Search forward for a block within max span and maturity window
207         while (pindex->pnext && (pindex->GetBlockTime() + CHECKPOINT_MAX_SPAN <= pindexBest->GetBlockTime() || pindex->nHeight + std::min(6, nCoinbaseMaturity - 20) <= pindexBest->nHeight))
208             pindex = pindex->pnext;
209         return pindex->GetBlockHash();
210     }
211
212     // Check against synchronized checkpoint
213     bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
214     {
215         if (fTestNet) return true; // Testnet has no checkpoints
216         int nHeight = pindexPrev->nHeight + 1;
217
218         LOCK(cs_hashSyncCheckpoint);
219         // sync-checkpoint should always be accepted block
220         assert(mapBlockIndex.count(hashSyncCheckpoint));
221         const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
222
223         if (nHeight > pindexSync->nHeight)
224         {
225             // trace back to same height as sync-checkpoint
226             const CBlockIndex* pindex = pindexPrev;
227             while (pindex->nHeight > pindexSync->nHeight)
228                 if (!(pindex = pindex->pprev))
229                     return error("CheckSync: pprev null - block index structure failure");
230             if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
231                 return false; // only descendant of sync-checkpoint can pass check
232         }
233         if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
234             return false; // same height with sync-checkpoint
235         if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
236             return false; // lower height than sync-checkpoint
237         return true;
238     }
239
240     bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
241     {
242         LOCK(cs_hashSyncCheckpoint);
243         if (hashPendingCheckpoint == 0)
244             return false;
245         if (hashBlock == hashPendingCheckpoint)
246             return true;
247         if (mapOrphanBlocks.count(hashPendingCheckpoint) 
248             && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
249             return true;
250         return false;
251     }
252
253     // ppcoin: reset synchronized checkpoint to last hardened checkpoint
254     bool ResetSyncCheckpoint()
255     {
256         LOCK(cs_hashSyncCheckpoint);
257         const uint256& hash = mapCheckpoints.rbegin()->second;
258         if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
259         {
260             // checkpoint block accepted but not yet in main chain
261             printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str());
262             CTxDB txdb;
263             CBlock block;
264             if (!block.ReadFromDisk(mapBlockIndex[hash]))
265                 return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str());
266             if (!block.SetBestChain(txdb, mapBlockIndex[hash]))
267             {
268                 return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str());
269             }
270             txdb.Close();
271         }
272         else if(!mapBlockIndex.count(hash))
273         {
274             // checkpoint block not yet accepted
275             hashPendingCheckpoint = hash;
276             checkpointMessagePending.SetNull();
277             printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str());
278         }
279
280         BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
281         {
282             const uint256& hash = i.second;
283             if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
284             {
285                 if (!WriteSyncCheckpoint(hash))
286                     return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str());
287                 printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str());
288                 return true;
289             }
290         }
291
292         return false;
293     }
294
295     void AskForPendingSyncCheckpoint(CNode* pfrom)
296     {
297         LOCK(cs_hashSyncCheckpoint);
298         if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
299             pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
300     }
301
302     bool SetCheckpointPrivKey(std::string strPrivKey)
303     {
304         // Test signing a sync-checkpoint with genesis block
305         CSyncCheckpoint checkpoint;
306         checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet;
307         CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
308         sMsg << (CUnsignedSyncCheckpoint)checkpoint;
309         checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
310
311         std::vector<unsigned char> vchPrivKey = ParseHex(strPrivKey);
312         CKey key;
313         key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
314         if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
315             return false;
316
317         // Test signing successful, proceed
318         CSyncCheckpoint::strMasterPrivKey = strPrivKey;
319         return true;
320     }
321
322     bool SendSyncCheckpoint(uint256 hashCheckpoint)
323     {
324         CSyncCheckpoint checkpoint;
325         checkpoint.hashCheckpoint = hashCheckpoint;
326         CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
327         sMsg << (CUnsignedSyncCheckpoint)checkpoint;
328         checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
329
330         if (CSyncCheckpoint::strMasterPrivKey.empty())
331             return error("SendSyncCheckpoint: Checkpoint master key unavailable.");
332         std::vector<unsigned char> vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey);
333         CKey key;
334         key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
335         if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
336             return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?");
337
338         if(!checkpoint.ProcessSyncCheckpoint(NULL))
339         {
340             printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n");
341             return false;
342         }
343
344         // Relay checkpoint
345         {
346             LOCK(cs_vNodes);
347             BOOST_FOREACH(CNode* pnode, vNodes)
348                 checkpoint.RelayTo(pnode);
349         }
350         return true;
351     }
352
353     // Is the sync-checkpoint outside maturity window?
354     bool IsMatureSyncCheckpoint()
355     {
356         LOCK(cs_hashSyncCheckpoint);
357         // sync-checkpoint should always be accepted block
358         assert(mapBlockIndex.count(hashSyncCheckpoint));
359         const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
360         return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity ||
361                 pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime());
362     }
363
364     // Is the sync-checkpoint too old?
365     bool IsSyncCheckpointTooOld(unsigned int nSeconds)
366     {
367         LOCK(cs_hashSyncCheckpoint);
368         // sync-checkpoint should always be accepted block
369         assert(mapBlockIndex.count(hashSyncCheckpoint));
370         const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
371         return (pindexSync->GetBlockTime() + nSeconds < GetAdjustedTime());
372     }
373 }
374
375 // ppcoin: sync-checkpoint master key
376 const std::string CSyncCheckpoint::strMasterPubKey = "04a51b735f816de4ec3f891d5b38bbc91e1f7245c7c08d17990760b86b4d8fc3910a850ffecf73bfa8886f01739a0c4c4322201282d07b6e48ce931cc92af94850";
377
378 std::string CSyncCheckpoint::strMasterPrivKey = "";
379
380 // ppcoin: verify signature of sync-checkpoint message
381 bool CSyncCheckpoint::CheckSignature()
382 {
383     CKey key;
384     if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey)))
385         return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed");
386     if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
387         return error("CSyncCheckpoint::CheckSignature() : verify signature failed");
388
389     // Now unserialize the data
390     CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
391     sMsg >> *(CUnsignedSyncCheckpoint*)this;
392     return true;
393 }
394
395 // ppcoin: process synchronized checkpoint
396 bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
397 {
398     if (!CheckSignature())
399         return false;
400
401     LOCK(Checkpoints::cs_hashSyncCheckpoint);
402     if (!mapBlockIndex.count(hashCheckpoint))
403     {
404         // We haven't received the checkpoint chain, keep the checkpoint as pending
405         Checkpoints::hashPendingCheckpoint = hashCheckpoint;
406         Checkpoints::checkpointMessagePending = *this;
407         printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str());
408         // Ask this guy to fill in what we're missing
409         if (pfrom)
410         {
411             pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
412             // ask directly as well in case rejected earlier by duplicate
413             // proof-of-stake because getblocks may not get it this time
414             pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
415         }
416         return false;
417     }
418
419     if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
420         return false;
421
422     CTxDB txdb;
423     CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
424     if (!pindexCheckpoint->IsInMainChain())
425     {
426         // checkpoint chain received but not yet main chain
427         CBlock block;
428         if (!block.ReadFromDisk(pindexCheckpoint))
429             return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
430         if (!block.SetBestChain(txdb, pindexCheckpoint))
431         {
432             Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
433             return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
434         }
435     }
436     txdb.Close();
437
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());
444     return true;
445 }