a721cc48534116052f435de67bda85f226d12ef1
[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 "txdb.h"
11 #include "main.h"
12 #include "uint256.h"
13
14 namespace Checkpoints
15 {
16     typedef std::map<int, std::pair<uint256, unsigned int> > 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,     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) )
32     ;
33
34     // TestNet has no checkpoints
35     static MapCheckpoints mapCheckpointsTestnet =
36         boost::assign::map_list_of
37         ( 0, std::make_pair(hashGenesisBlockTestNet, 1360105017) )
38         ;
39
40     bool CheckHardened(int nHeight, const uint256& hash)
41     {
42         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
43
44         MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
45         if (i == checkpoints.end()) return true;
46         return hash == i->second.first;
47     }
48
49     int GetTotalBlocksEstimate()
50     {
51         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
52
53         return checkpoints.rbegin()->first;
54     }
55
56     unsigned int GetLastCheckpointTime()
57     {
58         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
59
60         return checkpoints.rbegin()->second.second;
61     }
62
63     CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
64     {
65         MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
66
67         BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
68         {
69             const uint256& hash = i.second.first;
70             std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
71             if (t != mapBlockIndex.end())
72                 return t->second;
73         }
74         return NULL;
75     }
76
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;
84
85     // ppcoin: get last synchronized checkpoint
86     CBlockIndex* GetLastSyncCheckpoint()
87     {
88         LOCK(cs_hashSyncCheckpoint);
89         if (!mapBlockIndex.count(hashSyncCheckpoint))
90             error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
91         else
92             return mapBlockIndex[hashSyncCheckpoint];
93         return NULL;
94     }
95
96     // ppcoin: only descendant of current sync-checkpoint is allowed
97     bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
98     {
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());
103
104         CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
105         CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
106
107         if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
108         {
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)
117             {
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());
120             }
121             return false; // ignore older checkpoint
122         }
123
124         // Received checkpoint should be a descendant block of the current
125         // checkpoint. Trace back to the same height of current checkpoint
126         // to verify.
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)
132         {
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());
135         }
136         return true;
137     }
138
139     bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
140     {
141         CTxDB txdb;
142         txdb.TxnBegin();
143         if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
144         {
145             txdb.TxnAbort();
146             return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
147         }
148         if (!txdb.TxnCommit())
149             return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
150
151 #ifndef USE_LEVELDB
152         txdb.Close();
153 #endif
154
155         Checkpoints::hashSyncCheckpoint = hashCheckpoint;
156         return true;
157     }
158
159     bool AcceptPendingSyncCheckpoint()
160     {
161         LOCK(cs_hashSyncCheckpoint);
162         if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
163         {
164             if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
165             {
166                 hashPendingCheckpoint = 0;
167                 checkpointMessagePending.SetNull();
168                 return false;
169             }
170
171             CTxDB txdb;
172             CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
173             if (!pindexCheckpoint->IsInMainChain())
174             {
175                 CBlock block;
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))
179                 {
180                     hashInvalidCheckpoint = hashPendingCheckpoint;
181                     return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
182                 }
183             }
184
185 #ifndef USE_LEVELDB
186             txdb.Close();
187 #endif
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())
196             {
197                 BOOST_FOREACH(CNode* pnode, vNodes)
198                     checkpointMessage.RelayTo(pnode);
199             }
200             return true;
201         }
202         return false;
203     }
204
205     // Automatically select a suitable sync-checkpoint 
206     uint256 AutoSelectSyncCheckpoint()
207     {
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();
213     }
214
215     // Check against synchronized checkpoint
216     bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
217     {
218         if (fTestNet) return true; // Testnet has no checkpoints
219         int nHeight = pindexPrev->nHeight + 1;
220
221         LOCK(cs_hashSyncCheckpoint);
222         // sync-checkpoint should always be accepted block
223         assert(mapBlockIndex.count(hashSyncCheckpoint));
224         const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
225
226         if (nHeight > pindexSync->nHeight)
227         {
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
235         }
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
240         return true;
241     }
242
243     bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
244     {
245         LOCK(cs_hashSyncCheckpoint);
246         if (hashPendingCheckpoint == 0)
247             return false;
248         if (hashBlock == hashPendingCheckpoint)
249             return true;
250         if (mapOrphanBlocks.count(hashPendingCheckpoint) 
251             && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
252             return true;
253         return false;
254     }
255
256     // ppcoin: reset synchronized checkpoint to last hardened checkpoint
257     bool ResetSyncCheckpoint()
258     {
259         LOCK(cs_hashSyncCheckpoint);
260         const uint256& hash = mapCheckpoints.rbegin()->second.first;
261         if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
262         {
263             // checkpoint block accepted but not yet in main chain
264             printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str());
265             CTxDB txdb;
266             CBlock block;
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]))
270             {
271                 return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str());
272             }
273
274 #ifndef USE_LEVELDB
275             txdb.Close();
276 #endif
277
278         }
279         else if(!mapBlockIndex.count(hash))
280         {
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());
285         }
286
287         BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
288         {
289             const uint256& hash = i.second.first;
290             if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
291             {
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());
295                 return true;
296             }
297         }
298
299         return false;
300     }
301
302     void AskForPendingSyncCheckpoint(CNode* pfrom)
303     {
304         LOCK(cs_hashSyncCheckpoint);
305         if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
306             pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
307     }
308
309     bool SetCheckpointPrivKey(std::string strPrivKey)
310     {
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());
317
318         std::vector<unsigned char> vchPrivKey = ParseHex(strPrivKey);
319         CKey key;
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))
322             return false;
323
324         // Test signing successful, proceed
325         CSyncCheckpoint::strMasterPrivKey = strPrivKey;
326         return true;
327     }
328
329     bool SendSyncCheckpoint(uint256 hashCheckpoint)
330     {
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());
336
337         if (CSyncCheckpoint::strMasterPrivKey.empty())
338             return error("SendSyncCheckpoint: Checkpoint master key unavailable.");
339         std::vector<unsigned char> vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey);
340         CKey key;
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?");
344
345         if(!checkpoint.ProcessSyncCheckpoint(NULL))
346         {
347             printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n");
348             return false;
349         }
350
351         // Relay checkpoint
352         {
353             LOCK(cs_vNodes);
354             BOOST_FOREACH(CNode* pnode, vNodes)
355                 checkpoint.RelayTo(pnode);
356         }
357         return true;
358     }
359
360     // Is the sync-checkpoint outside maturity window?
361     bool IsMatureSyncCheckpoint()
362     {
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());
369     }
370 }
371
372 // ppcoin: sync-checkpoint master key
373 const std::string CSyncCheckpoint::strMasterPubKey = "04a51b735f816de4ec3f891d5b38bbc91e1f7245c7c08d17990760b86b4d8fc3910a850ffecf73bfa8886f01739a0c4c4322201282d07b6e48ce931cc92af94850";
374
375 std::string CSyncCheckpoint::strMasterPrivKey = "";
376
377 // ppcoin: verify signature of sync-checkpoint message
378 bool CSyncCheckpoint::CheckSignature()
379 {
380     CKey key;
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");
385
386     // Now unserialize the data
387     CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
388     sMsg >> *(CUnsignedSyncCheckpoint*)this;
389     return true;
390 }
391
392 // process synchronized checkpoint
393 bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
394 {
395     if (!CheckSignature())
396         return false;
397
398     LOCK(Checkpoints::cs_hashSyncCheckpoint);
399     if (!mapBlockIndex.count(hashCheckpoint))
400     {
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
406         if (pfrom)
407         {
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));
412         }
413         return false;
414     }
415
416     if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
417         return false;
418
419     CTxDB txdb;
420     CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
421     if (!pindexCheckpoint->IsInMainChain())
422     {
423         // checkpoint chain received but not yet main chain
424         CBlock block;
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))
428         {
429             Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
430             return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
431         }
432     }
433
434 #ifndef USE_LEVELDB
435     txdb.Close();
436 #endif
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 }