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