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