Batch block connection during initial block download
[novacoin.git] / src / db.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "db.h"
7 #include "util.h"
8 #include "main.h"
9 #include "kernel.h"
10 #include "checkpoints.h"
11 #include <boost/version.hpp>
12 #include <boost/filesystem.hpp>
13 #include <boost/filesystem/fstream.hpp>
14
15 #ifndef WIN32
16 #include "sys/stat.h"
17 #endif
18
19 using namespace std;
20 using namespace boost;
21
22
23 unsigned int nWalletDBUpdated;
24
25
26
27 //
28 // CDB
29 //
30
31 CDBEnv bitdb;
32
33 void CDBEnv::EnvShutdown()
34 {
35     if (!fDbEnvInit)
36         return;
37
38     fDbEnvInit = false;
39     int ret = dbenv.close(0);
40     if (ret != 0)
41         printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
42     if (!fMockDb)
43         DbEnv(0).remove(GetDataDir().string().c_str(), 0);
44 }
45
46 CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
47 {
48 }
49
50 CDBEnv::~CDBEnv()
51 {
52     EnvShutdown();
53 }
54
55 void CDBEnv::Close()
56 {
57     EnvShutdown();
58 }
59
60 bool CDBEnv::Open(boost::filesystem::path pathEnv_)
61 {
62     if (fDbEnvInit)
63         return true;
64
65     if (fShutdown)
66         return false;
67
68     pathEnv = pathEnv_;
69     filesystem::path pathDataDir = pathEnv;
70     filesystem::path pathLogDir = pathDataDir / "database";
71     filesystem::create_directory(pathLogDir);
72     filesystem::path pathErrorFile = pathDataDir / "db.log";
73     printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
74
75     unsigned int nEnvFlags = 0;
76     if (GetBoolArg("-privdb", true))
77         nEnvFlags |= DB_PRIVATE;
78
79     int nDbCache = GetArg("-dbcache", 25);
80     dbenv.set_lg_dir(pathLogDir.string().c_str());
81     dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
82     dbenv.set_lg_bsize(1048576);
83     dbenv.set_lg_max(10485760);
84     dbenv.set_lk_max_locks(40000);
85     dbenv.set_lk_max_objects(40000);
86     dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
87     dbenv.set_flags(DB_AUTO_COMMIT, 1);
88     dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
89 #ifdef DB_LOG_AUTO_REMOVE
90     dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
91 #endif
92     int ret = dbenv.open(pathDataDir.string().c_str(),
93                      DB_CREATE     |
94                      DB_INIT_LOCK  |
95                      DB_INIT_LOG   |
96                      DB_INIT_MPOOL |
97                      DB_INIT_TXN   |
98                      DB_THREAD     |
99                      DB_RECOVER    |
100                      nEnvFlags,
101                      S_IRUSR | S_IWUSR);
102     if (ret != 0)
103         return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
104
105     fDbEnvInit = true;
106     fMockDb = false;
107     return true;
108 }
109
110 void CDBEnv::MakeMock()
111 {
112     if (fDbEnvInit)
113         throw runtime_error("CDBEnv::MakeMock(): already initialized");
114
115     if (fShutdown)
116         throw runtime_error("CDBEnv::MakeMock(): during shutdown");
117
118     printf("CDBEnv::MakeMock()\n");
119
120     dbenv.set_cachesize(1, 0, 1);
121     dbenv.set_lg_bsize(10485760*4);
122     dbenv.set_lg_max(10485760);
123     dbenv.set_lk_max_locks(10000);
124     dbenv.set_lk_max_objects(10000);
125     dbenv.set_flags(DB_AUTO_COMMIT, 1);
126 #ifdef DB_LOG_IN_MEMORY
127     dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
128 #endif
129     int ret = dbenv.open(NULL,
130                      DB_CREATE     |
131                      DB_INIT_LOCK  |
132                      DB_INIT_LOG   |
133                      DB_INIT_MPOOL |
134                      DB_INIT_TXN   |
135                      DB_THREAD     |
136                      DB_PRIVATE,
137                      S_IRUSR | S_IWUSR);
138     if (ret > 0)
139         throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
140
141     fDbEnvInit = true;
142     fMockDb = true;
143 }
144
145 CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
146 {
147     LOCK(cs_db);
148     assert(mapFileUseCount.count(strFile) == 0);
149
150     Db db(&dbenv, 0);
151     int result = db.verify(strFile.c_str(), NULL, NULL, 0);
152     if (result == 0)
153         return VERIFY_OK;
154     else if (recoverFunc == NULL)
155         return RECOVER_FAIL;
156
157     // Try to recover:
158     bool fRecovered = (*recoverFunc)(*this, strFile);
159     return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
160 }
161
162 bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
163                      std::vector<CDBEnv::KeyValPair >& vResult)
164 {
165     LOCK(cs_db);
166     assert(mapFileUseCount.count(strFile) == 0);
167
168     u_int32_t flags = DB_SALVAGE;
169     if (fAggressive) flags |= DB_AGGRESSIVE;
170
171     stringstream strDump;
172
173     Db db(&dbenv, 0);
174     int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
175     if (result != 0)
176     {
177         printf("ERROR: db salvage failed\n");
178         return false;
179     }
180
181     // Format of bdb dump is ascii lines:
182     // header lines...
183     // HEADER=END
184     // hexadecimal key
185     // hexadecimal value
186     // ... repeated
187     // DATA=END
188
189     string strLine;
190     while (!strDump.eof() && strLine != "HEADER=END")
191         getline(strDump, strLine); // Skip past header
192
193     std::string keyHex, valueHex;
194     while (!strDump.eof() && keyHex != "DATA=END")
195     {
196         getline(strDump, keyHex);
197         if (keyHex != "DATA_END")
198         {
199             getline(strDump, valueHex);
200             vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
201         }
202     }
203
204     return (result == 0);
205 }
206
207
208 void CDBEnv::CheckpointLSN(std::string strFile)
209 {
210     dbenv.txn_checkpoint(0, 0, 0);
211     if (fMockDb)
212         return;
213     dbenv.lsn_reset(strFile.c_str(), 0);
214 }
215
216
217 CDB::CDB(const char *pszFile, const char* pszMode) :
218     pdb(NULL), activeTxn(NULL)
219 {
220     int ret;
221     if (pszFile == NULL)
222         return;
223
224     fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
225     bool fCreate = strchr(pszMode, 'c');
226     unsigned int nFlags = DB_THREAD;
227     if (fCreate)
228         nFlags |= DB_CREATE;
229
230     {
231         LOCK(bitdb.cs_db);
232         if (!bitdb.Open(GetDataDir()))
233             throw runtime_error("env open failed");
234
235         strFile = pszFile;
236         ++bitdb.mapFileUseCount[strFile];
237         pdb = bitdb.mapDb[strFile];
238         if (pdb == NULL)
239         {
240             pdb = new Db(&bitdb.dbenv, 0);
241
242             bool fMockDb = bitdb.IsMock();
243             if (fMockDb)
244             {
245                 DbMpoolFile*mpf = pdb->get_mpf();
246                 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
247                 if (ret != 0)
248                     throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
249             }
250
251             ret = pdb->open(NULL,      // Txn pointer
252                             fMockDb ? NULL : pszFile,   // Filename
253                             fMockDb ? pszFile : "main", // Logical db name
254                             DB_BTREE,  // Database type
255                             nFlags,    // Flags
256                             0);
257
258             if (ret != 0)
259             {
260                 delete pdb;
261                 pdb = NULL;
262                 --bitdb.mapFileUseCount[strFile];
263                 strFile = "";
264                 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
265             }
266
267             if (fCreate && !Exists(string("version")))
268             {
269                 bool fTmp = fReadOnly;
270                 fReadOnly = false;
271                 WriteVersion(CLIENT_VERSION);
272                 fReadOnly = fTmp;
273             }
274
275             bitdb.mapDb[strFile] = pdb;
276         }
277     }
278 }
279
280 static bool IsChainFile(std::string strFile)
281 {
282     if (strFile == "coins.dat" || strFile == "chain.dat")
283         return true;
284
285     return false;
286 }
287
288 void CDB::Flush()
289 {
290     if (activeTxn)
291         return;
292
293     // Flush database activity from memory pool to disk log
294     unsigned int nMinutes = 0;
295     if (fReadOnly)
296         nMinutes = 1;
297     if (IsChainFile(strFile))
298         nMinutes = 2;
299     if (IsChainFile(strFile) && IsInitialBlockDownload())
300         nMinutes = 5;
301
302     bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
303 }
304
305 void CDB::Close()
306 {
307     if (!pdb)
308         return;
309     if (activeTxn)
310         activeTxn->abort();
311     activeTxn = NULL;
312     pdb = NULL;
313
314     Flush();
315
316     {
317         LOCK(bitdb.cs_db);
318         --bitdb.mapFileUseCount[strFile];
319     }
320 }
321
322 void CDBEnv::CloseDb(const string& strFile)
323 {
324     {
325         LOCK(cs_db);
326         if (mapDb[strFile] != NULL)
327         {
328             // Close the database handle
329             Db* pdb = mapDb[strFile];
330             pdb->close(0);
331             delete pdb;
332             mapDb[strFile] = NULL;
333         }
334     }
335 }
336
337 bool CDBEnv::RemoveDb(const string& strFile)
338 {
339     this->CloseDb(strFile);
340
341     LOCK(cs_db);
342     int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
343     return (rc == 0);
344 }
345
346 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
347 {
348     while (!fShutdown)
349     {
350         {
351             LOCK(bitdb.cs_db);
352             if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
353             {
354                 // Flush log data to the dat file
355                 bitdb.CloseDb(strFile);
356                 bitdb.CheckpointLSN(strFile);
357                 bitdb.mapFileUseCount.erase(strFile);
358
359                 bool fSuccess = true;
360                 printf("Rewriting %s...\n", strFile.c_str());
361                 string strFileRes = strFile + ".rewrite";
362                 { // surround usage of db with extra {}
363                     CDB db(strFile.c_str(), "r");
364                     Db* pdbCopy = new Db(&bitdb.dbenv, 0);
365
366                     int ret = pdbCopy->open(NULL,                 // Txn pointer
367                                             strFileRes.c_str(),   // Filename
368                                             "main",    // Logical db name
369                                             DB_BTREE,  // Database type
370                                             DB_CREATE,    // Flags
371                                             0);
372                     if (ret > 0)
373                     {
374                         printf("Cannot create database file %s\n", strFileRes.c_str());
375                         fSuccess = false;
376                     }
377
378                     Dbc* pcursor = db.GetCursor();
379                     if (pcursor)
380                         while (fSuccess)
381                         {
382                             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
383                             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
384                             int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
385                             if (ret == DB_NOTFOUND)
386                             {
387                                 pcursor->close();
388                                 break;
389                             }
390                             else if (ret != 0)
391                             {
392                                 pcursor->close();
393                                 fSuccess = false;
394                                 break;
395                             }
396                             if (pszSkip &&
397                                 strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
398                                 continue;
399                             if (strncmp(&ssKey[0], "\x07version", 8) == 0)
400                             {
401                                 // Update version:
402                                 ssValue.clear();
403                                 ssValue << CLIENT_VERSION;
404                             }
405                             Dbt datKey(&ssKey[0], ssKey.size());
406                             Dbt datValue(&ssValue[0], ssValue.size());
407                             int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
408                             if (ret2 > 0)
409                                 fSuccess = false;
410                         }
411                     if (fSuccess)
412                     {
413                         db.Close();
414                         bitdb.CloseDb(strFile);
415                         if (pdbCopy->close(0))
416                             fSuccess = false;
417                         delete pdbCopy;
418                     }
419                 }
420                 if (fSuccess)
421                 {
422                     Db dbA(&bitdb.dbenv, 0);
423                     if (dbA.remove(strFile.c_str(), NULL, 0))
424                         fSuccess = false;
425                     Db dbB(&bitdb.dbenv, 0);
426                     if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
427                         fSuccess = false;
428                 }
429                 if (!fSuccess)
430                     printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
431                 return fSuccess;
432             }
433         }
434         Sleep(100);
435     }
436     return false;
437 }
438
439
440 void CDBEnv::Flush(bool fShutdown)
441 {
442     int64 nStart = GetTimeMillis();
443     // Flush log data to the actual data file
444     //  on all files that are not in use
445     printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
446     if (!fDbEnvInit)
447         return;
448     {
449         LOCK(cs_db);
450         map<string, int>::iterator mi = mapFileUseCount.begin();
451         while (mi != mapFileUseCount.end())
452         {
453             string strFile = (*mi).first;
454             int nRefCount = (*mi).second;
455             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
456             if (nRefCount == 0)
457             {
458                 // Move log data to the dat file
459                 CloseDb(strFile);
460                 printf("%s checkpoint\n", strFile.c_str());
461                 dbenv.txn_checkpoint(0, 0, 0);
462                 if (!IsChainFile(strFile) || fDetachDB) {
463                     printf("%s detach\n", strFile.c_str());
464                     if (!fMockDb)
465                         dbenv.lsn_reset(strFile.c_str(), 0);
466                 }
467                 printf("%s closed\n", strFile.c_str());
468                 mapFileUseCount.erase(mi++);
469             }
470             else
471                 mi++;
472         }
473         printf("DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
474         if (fShutdown)
475         {
476             char** listp;
477             if (mapFileUseCount.empty())
478             {
479                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
480                 Close();
481             }
482         }
483     }
484 }
485
486
487
488
489
490
491 //
492 // CChainDB and CCoinsDB
493 //
494
495 bool CCoinsDB::HaveCoins(uint256 hash) {
496     assert(!fClient);
497     return Exists(make_pair('c', hash));
498 }
499
500 bool CCoinsDB::ReadCoins(uint256 hash, CCoins &coins) {
501     assert(!fClient);
502     return Read(make_pair('c', hash), coins);
503 }
504
505 bool CCoinsDB::WriteCoins(uint256 hash, const CCoins &coins) {
506     assert(!fClient);
507     if (coins.IsPruned())
508         return Erase(make_pair('c', hash));
509     else
510         return Write(make_pair('c', hash), coins);
511 }
512
513 bool CChainDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
514 {
515     return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
516 }
517
518 bool CCoinsDB::ReadHashBestChain(uint256& hashBestChain)
519 {
520     return Read('B', hashBestChain);
521 }
522
523 bool CCoinsDB::WriteHashBestChain(uint256 hashBestChain)
524 {
525     return Write('B', hashBestChain);
526 }
527
528 bool CChainDB::ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust)
529 {
530     return Read('I', bnBestInvalidTrust);
531 }
532
533 bool CChainDB::WriteBestInvalidTrust(CBigNum bnBestInvalidTrust)
534 {
535     return Write('I', bnBestInvalidTrust);
536 }
537
538 bool CChainDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
539     return Write(make_pair('f', nFile), info);
540 }
541
542 bool CChainDB::ReadSyncCheckpoint(uint256& hashCheckpoint)
543 {
544     return Read('H', hashCheckpoint);
545 }
546
547 bool CChainDB::WriteSyncCheckpoint(uint256 hashCheckpoint)
548 {
549     return Write('H', hashCheckpoint);
550 }
551
552 bool CChainDB::ReadCheckpointPubKey(string& strPubKey)
553 {
554     return Read('K', strPubKey);
555 }
556
557 bool CChainDB::WriteCheckpointPubKey(const string& strPubKey)
558 {
559     return Write('K', strPubKey);
560 }
561
562
563 bool CChainDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
564     return Read(make_pair('f', nFile), info);
565 }
566
567 bool CChainDB::WriteLastBlockFile(int nFile) {
568     return Write('l', nFile);
569 }
570
571 bool CChainDB::ReadLastBlockFile(int &nFile) {
572     return Read('l', nFile);
573 }
574
575 CCoinsViewDB::CCoinsViewDB() : db("cr+") {}
576 bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { return db.ReadCoins(txid, coins); }
577 bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { return db.WriteCoins(txid, coins); }
578 bool CCoinsViewDB::HaveCoins(uint256 txid) { return db.HaveCoins(txid); }
579 CBlockIndex *CCoinsViewDB::GetBestBlock() {
580     uint256 hashBestChain;
581     if (!db.ReadHashBestChain(hashBestChain))
582         return NULL;
583     std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain);
584     if (it == mapBlockIndex.end())
585         return NULL;
586     return it->second;
587 }
588 bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { return db.WriteHashBestChain(pindex->GetBlockHash()); }
589 bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
590     printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size());
591
592     if (!db.TxnBegin())
593         return false;
594     bool fOk = true;
595     for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) {
596         fOk = db.WriteCoins(it->first, it->second);
597         if (!fOk)
598             break;
599     }
600     if (fOk)
601         fOk = db.WriteHashBestChain(pindex->GetBlockHash());
602
603     if (!fOk)
604         db.TxnAbort();
605     else
606         fOk = db.TxnCommit();
607
608     return fOk;
609 }
610
611 CBlockIndex static * InsertBlockIndex(uint256 hash)
612 {
613     if (hash == 0)
614         return NULL;
615
616     // Return existing
617     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
618     if (mi != mapBlockIndex.end())
619         return (*mi).second;
620
621     // Create new
622     CBlockIndex* pindexNew = new CBlockIndex();
623     if (!pindexNew)
624         throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
625     mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
626     pindexNew->phashBlock = &((*mi).first);
627
628     return pindexNew;
629 }
630
631 bool LoadBlockIndex(CChainDB &chaindb)
632 {
633     if (!chaindb.LoadBlockIndexGuts())
634         return false;
635
636     if (fRequestShutdown)
637         return true;
638
639     // Calculate nChainTrust
640     vector<pair<int, CBlockIndex*> > vSortedByHeight;
641     vSortedByHeight.reserve(mapBlockIndex.size());
642     BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
643     {
644         CBlockIndex* pindex = item.second;
645         vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
646     }
647     sort(vSortedByHeight.begin(), vSortedByHeight.end());
648     BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
649     {
650         CBlockIndex* pindex = item.second;
651         pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust();
652
653         // Calculate stake modifier checksum
654         pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
655         if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
656             return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindex->nHeight, pindex->nStakeModifier);
657     }
658
659     // Load block file info
660     chaindb.ReadLastBlockFile(nLastBlockFile);
661     printf("LoadBlockIndex(): last block file = %i\n", nLastBlockFile);
662     if (chaindb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
663         printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
664  
665     // Load hashBestChain pointer to end of best chain
666     pindexBest = pcoinsTip->GetBestBlock();
667     if (pindexBest == NULL)
668     {
669         if (pindexGenesisBlock == NULL)
670             return true;
671         return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
672     }
673     hashBestChain = pindexBest->GetBlockHash();
674     nBestHeight = pindexBest->nHeight;
675     nBestChainTrust = pindexBest->nChainTrust;
676
677     // set 'next' pointers in best chain
678     CBlockIndex *pindex = pindexBest;
679     while(pindex != NULL && pindex->pprev != NULL) {
680          CBlockIndex *pindexPrev = pindex->pprev;
681          pindexPrev->pnext = pindex;
682          pindex = pindexPrev;
683     }
684     printf("LoadBlockIndex(): hashBestChain=%s  height=%d date=%s\n",
685         hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
686         DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
687
688     // Load sync-checkpoint
689     if (!chaindb.ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
690         return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded");
691     printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
692
693     // Load bnBestInvalidTrust, OK if it doesn't exist
694     CBigNum bnBestInvalidTrust;
695     chaindb.ReadBestInvalidTrust(bnBestInvalidTrust);
696     nBestInvalidTrust = bnBestInvalidTrust.getuint256();
697
698     // Verify blocks in the best chain
699     int nCheckLevel = GetArg("-checklevel", 1);
700     int nCheckDepth = GetArg( "-checkblocks", 2500);
701     if (nCheckDepth == 0)
702         nCheckDepth = 1000000000; // suffices until the year 19000
703     if (nCheckDepth > nBestHeight)
704         nCheckDepth = nBestHeight;
705     printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
706     CBlockIndex* pindexFork = NULL;
707     for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
708     {
709         if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
710             break;
711         CBlock block;
712         if (!block.ReadFromDisk(pindex))
713             return error("LoadBlockIndex() : block.ReadFromDisk failed");
714         // check level 1: verify block validity
715         if (nCheckLevel>0 && !block.CheckBlock())
716         {
717             printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
718             pindexFork = pindex->pprev;
719         }
720         // TODO: stronger verifications
721     }
722     if (pindexFork && !fRequestShutdown)
723     {
724         // TODO: reorg back
725         return error("LoadBlockIndex(): chain database corrupted");
726     }
727
728     return true;
729 }
730
731
732
733 bool CChainDB::LoadBlockIndexGuts()
734 {
735     // Get database cursor
736     Dbc* pcursor = GetCursor();
737     if (!pcursor)
738         return false;
739
740     // Load mapBlockIndex
741     unsigned int fFlags = DB_SET_RANGE;
742     while (true)
743     {
744         // Read next record
745         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
746         if (fFlags == DB_SET_RANGE)
747             ssKey << make_pair('b', uint256(0));
748         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
749         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
750         fFlags = DB_NEXT;
751         if (ret == DB_NOTFOUND)
752             break;
753         else if (ret != 0)
754             return false;
755
756         // Unserialize
757
758         try {
759         char chType;
760         ssKey >> chType;
761         if (chType == 'b' && !fRequestShutdown)
762         {
763             CDiskBlockIndex diskindex;
764             ssValue >> diskindex;
765
766             uint256 blockHash = diskindex.GetBlockHash();
767
768             // Construct block index object
769             CBlockIndex* pindexNew = InsertBlockIndex(blockHash);
770             pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);
771             pindexNew->nHeight        = diskindex.nHeight;
772             pindexNew->pos            = diskindex.pos;
773             pindexNew->nUndoPos       = diskindex.nUndoPos;
774             pindexNew->nMint          = diskindex.nMint;
775             pindexNew->nMoneySupply   = diskindex.nMoneySupply;
776             pindexNew->nFlags         = diskindex.nFlags;
777             pindexNew->nStakeModifier = diskindex.nStakeModifier;
778             pindexNew->prevoutStake   = diskindex.prevoutStake;
779             pindexNew->nStakeTime     = diskindex.nStakeTime;
780             pindexNew->hashProofOfStake = diskindex.hashProofOfStake;
781             pindexNew->nVersion       = diskindex.nVersion;
782             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
783             pindexNew->nTime          = diskindex.nTime;
784             pindexNew->nBits          = diskindex.nBits;
785             pindexNew->nNonce         = diskindex.nNonce;
786
787             // Watch for genesis block
788             if (pindexGenesisBlock == NULL && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
789                 pindexGenesisBlock = pindexNew;
790
791             if (!pindexNew->CheckIndex())
792                 return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
793
794             // Build setStakeSeen
795             if (pindexNew->IsProofOfStake())
796                 setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
797         }
798         else
799         {
800             break; // if shutdown requested or finished loading block index
801         }
802         }    // try
803         catch (std::exception &e) {
804             return error("%s() : deserialize error", __PRETTY_FUNCTION__);
805         }
806     }
807     pcursor->close();
808
809     return true;
810 }
811
812
813
814
815
816 //
817 // CAddrDB
818 //
819
820
821 CAddrDB::CAddrDB()
822 {
823     pathAddr = GetDataDir() / "peers.dat";
824 }
825
826 bool CAddrDB::Write(const CAddrMan& addr)
827 {
828     // Generate random temporary filename
829     unsigned short randv = 0;
830     RAND_bytes((unsigned char *)&randv, sizeof(randv));
831     std::string tmpfn = strprintf("peers.dat.%04x", randv);
832
833     // serialize addresses, checksum data up to that point, then append csum
834     CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
835     ssPeers << FLATDATA(pchMessageStart);
836     ssPeers << addr;
837     uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
838     ssPeers << hash;
839
840     // open temp output file, and associate with CAutoFile
841     boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
842     FILE *file = fopen(pathTmp.string().c_str(), "wb");
843     CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
844     if (!fileout)
845         return error("CAddrman::Write() : open failed");
846
847     // Write and commit header, data
848     try {
849         fileout << ssPeers;
850     }
851     catch (std::exception &e) {
852         return error("CAddrman::Write() : I/O error");
853     }
854     FileCommit(fileout);
855     fileout.fclose();
856
857     // replace existing peers.dat, if any, with new peers.dat.XXXX
858     if (!RenameOver(pathTmp, pathAddr))
859         return error("CAddrman::Write() : Rename-into-place failed");
860
861     return true;
862 }
863
864 bool CAddrDB::Read(CAddrMan& addr)
865 {
866     // open input file, and associate with CAutoFile
867     FILE *file = fopen(pathAddr.string().c_str(), "rb");
868     CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
869     if (!filein)
870         return error("CAddrman::Read() : open failed");
871
872     // use file size to size memory buffer
873     int fileSize = GetFilesize(filein);
874     int dataSize = fileSize - sizeof(uint256);
875     vector<unsigned char> vchData;
876     vchData.resize(dataSize);
877     uint256 hashIn;
878
879     // read data and checksum from file
880     try {
881         filein.read((char *)&vchData[0], dataSize);
882         filein >> hashIn;
883     }
884     catch (std::exception &e) {
885         return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
886     }
887     filein.fclose();
888
889     CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
890
891     // verify stored checksum matches input data
892     uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
893     if (hashIn != hashTmp)
894         return error("CAddrman::Read() : checksum mismatch; data corrupted");
895
896     // de-serialize address data
897     unsigned char pchMsgTmp[4];
898     try {
899         ssPeers >> FLATDATA(pchMsgTmp);
900         ssPeers >> addr;
901     }
902     catch (std::exception &e) {
903         return error("CAddrman::Read() : I/O error or stream data corrupted");
904     }
905
906     // finally, verify the network matches ours
907     if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
908         return error("CAddrman::Read() : invalid network magic number");
909
910     return true;
911 }
912