Remove BerkeleyDB support for block index
[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 "util.h"
9 #include "main.h"
10 #include "interface.h"
11 #include <boost/filesystem.hpp>
12 #include <boost/filesystem/fstream.hpp>
13
14 #ifndef WIN32
15 #include "sys/stat.h"
16 #endif
17
18 using namespace std;
19 using namespace boost;
20
21
22 unsigned int nWalletDBUpdated;
23 extern bool fUseMemoryLog;
24
25
26 //
27 // CDB
28 //
29
30 CDBEnv bitdb;
31
32 void CDBEnv::EnvShutdown()
33 {
34     if (!fDbEnvInit)
35         return;
36
37     fDbEnvInit = false;
38     int ret = dbenv.close(0);
39     if (ret != 0)
40         printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
41     if (!fMockDb)
42         DbEnv(0u).remove(strPath.c_str(), 0);
43 }
44
45 CDBEnv::CDBEnv() : fDetachDB(false), fDbEnvInit(false), fMockDb(false), dbenv(DB_CXX_NO_EXCEPTIONS) { }
46
47 CDBEnv::~CDBEnv()
48 {
49     EnvShutdown();
50 }
51
52 void CDBEnv::Close()
53 {
54     EnvShutdown();
55 }
56
57 bool CDBEnv::Open(boost::filesystem::path pathEnv_)
58 {
59     if (fDbEnvInit)
60         return true;
61
62     if (fShutdown)
63         return false;
64
65     pathEnv = pathEnv_;
66     boost::filesystem::path pathDataDir = pathEnv;
67     strPath = pathDataDir.string();
68     boost::filesystem::path pathLogDir = pathDataDir / "database";
69     boost::filesystem::create_directory(pathLogDir);
70     boost::filesystem::path pathErrorFile = pathDataDir / "db.log";
71     printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
72
73     unsigned int nEnvFlags = 0;
74     if (GetBoolArg("-privdb", true))
75         nEnvFlags |= DB_PRIVATE;
76
77     int nDbCache = GetArgInt("-dbcache", 25);
78     dbenv.set_lg_dir(pathLogDir.string().c_str());
79     dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
80     dbenv.set_lg_bsize(1048576);
81     dbenv.set_lg_max(10485760);
82
83     // Bugfix: Bump lk_max_locks default to 537000, to safely handle reorgs with up to 5 blocks reversed
84     // dbenv.set_lk_max_locks(10000);
85     dbenv.set_lk_max_locks(537000);
86
87     dbenv.set_lk_max_objects(10000);
88     dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
89     dbenv.set_flags(DB_AUTO_COMMIT, 1);
90     dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
91 #ifdef DB_LOG_AUTO_REMOVE
92     dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
93 #endif
94     int ret = dbenv.open(strPath.c_str(),
95                      DB_CREATE     |
96                      DB_INIT_LOCK  |
97                      DB_INIT_LOG   |
98                      DB_INIT_MPOOL |
99                      DB_INIT_TXN   |
100                      DB_THREAD     |
101                      DB_RECOVER    |
102                      nEnvFlags,
103                      S_IRUSR | S_IWUSR);
104     if (ret != 0)
105         return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
106
107     fDbEnvInit = true;
108     fMockDb = false;
109
110     return true;
111 }
112
113 void CDBEnv::MakeMock()
114 {
115     if (fDbEnvInit)
116         throw runtime_error("CDBEnv::MakeMock(): already initialized");
117
118     if (fShutdown)
119         throw runtime_error("CDBEnv::MakeMock(): during shutdown");
120
121     printf("CDBEnv::MakeMock()\n");
122
123     dbenv.set_cachesize(1, 0, 1);
124     dbenv.set_lg_bsize(10485760*4);
125     dbenv.set_lg_max(10485760);
126     dbenv.set_lk_max_locks(10000);
127     dbenv.set_lk_max_objects(10000);
128     dbenv.set_flags(DB_AUTO_COMMIT, 1);
129 #ifdef DB_LOG_IN_MEMORY
130     dbenv.log_set_config(DB_LOG_IN_MEMORY, fUseMemoryLog ? 1 : 0);
131 #endif
132     int ret = dbenv.open(NULL,
133                      DB_CREATE     |
134                      DB_INIT_LOCK  |
135                      DB_INIT_LOG   |
136                      DB_INIT_MPOOL |
137                      DB_INIT_TXN   |
138                      DB_THREAD     |
139                      DB_PRIVATE,
140                      S_IRUSR | S_IWUSR);
141     if (ret > 0)
142         throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
143
144     fDbEnvInit = true;
145     fMockDb = true;
146 }
147
148 CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
149 {
150     LOCK(cs_db);
151     assert(mapFileUseCount.count(strFile) == 0);
152
153     Db db(&dbenv, 0);
154     int result = db.verify(strFile.c_str(), NULL, NULL, 0);
155     if (result == 0)
156         return VERIFY_OK;
157     else if (recoverFunc == NULL)
158         return RECOVER_FAIL;
159
160     // Try to recover:
161     bool fRecovered = (*recoverFunc)(*this, strFile);
162     return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
163 }
164
165 bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
166                      std::vector<CDBEnv::KeyValPair >& vResult)
167 {
168     LOCK(cs_db);
169     assert(mapFileUseCount.count(strFile) == 0);
170
171     u_int32_t flags = DB_SALVAGE;
172     if (fAggressive) flags |= DB_AGGRESSIVE;
173
174     stringstream strDump;
175
176     Db db(&dbenv, 0);
177     int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
178     if (result != 0)
179     {
180         printf("ERROR: db salvage failed\n");
181         return false;
182     }
183
184     // Format of bdb dump is ascii lines:
185     // header lines...
186     // HEADER=END
187     // hexadecimal key
188     // hexadecimal value
189     // ... repeated
190     // DATA=END
191
192     string strLine;
193     while (!strDump.eof() && strLine != "HEADER=END")
194         getline(strDump, strLine); // Skip past header
195
196     std::string keyHex, valueHex;
197     while (!strDump.eof() && keyHex != "DATA=END")
198     {
199         getline(strDump, keyHex);
200         if (keyHex != "DATA=END")
201         {
202             getline(strDump, valueHex);
203             vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
204         }
205     }
206
207     return (result == 0);
208 }
209
210
211 void CDBEnv::CheckpointLSN(std::string strFile)
212 {
213     dbenv.txn_checkpoint(0, 0, 0);
214     if (fMockDb)
215         return;
216     dbenv.lsn_reset(strFile.c_str(), 0);
217 }
218
219
220 CDB::CDB(const char *pszFile, const char* pszMode) :
221     pdb(NULL), activeTxn(NULL)
222 {
223     int ret;
224     if (pszFile == NULL)
225         return;
226
227     fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
228     bool fCreate = strchr(pszMode, 'c') != NULL;
229     unsigned int nFlags = DB_THREAD;
230     if (fCreate)
231         nFlags |= DB_CREATE;
232
233     {
234         LOCK(bitdb.cs_db);
235         if (!bitdb.Open(GetDataDir()))
236             throw runtime_error("env open failed");
237
238         strFile = pszFile;
239         ++bitdb.mapFileUseCount[strFile];
240         pdb = bitdb.mapDb[strFile];
241         if (pdb == NULL)
242         {
243             pdb = new Db(&bitdb.dbenv, 0);
244
245             bool fMockDb = bitdb.IsMock();
246             if (fMockDb)
247             {
248                 DbMpoolFile*mpf = pdb->get_mpf();
249                 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
250                 if (ret != 0)
251                     throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
252             }
253
254             ret = pdb->open(NULL,      // Txn pointer
255                             fMockDb ? NULL : pszFile,   // Filename
256                             "main",    // Logical db name
257                             DB_BTREE,  // Database type
258                             nFlags,    // Flags
259                             0);
260
261             if (ret != 0)
262             {
263                 delete pdb;
264                 pdb = NULL;
265                 --bitdb.mapFileUseCount[strFile];
266                 strFile.clear();
267                 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
268             }
269
270             if (fCreate && !Exists(string("version")))
271             {
272                 bool fTmp = fReadOnly;
273                 fReadOnly = false;
274                 WriteVersion(CLIENT_VERSION);
275                 fReadOnly = fTmp;
276             }
277
278             bitdb.mapDb[strFile] = pdb;
279         }
280     }
281 }
282
283 static bool IsChainFile(std::string strFile)
284 {
285     if (strFile == "blkindex.dat")
286         return true;
287
288     return false;
289 }
290
291 void CDB::Close()
292 {
293     if (!pdb)
294         return;
295     if (activeTxn)
296         activeTxn->abort();
297     activeTxn = NULL;
298     pdb = NULL;
299
300     // Flush database activity from memory pool to disk log
301     unsigned int nMinutes = 0;
302     if (fReadOnly)
303         nMinutes = 1;
304     if (IsChainFile(strFile))
305         nMinutes = 2;
306     if (IsChainFile(strFile) && IsInitialBlockDownload())
307         nMinutes = 5;
308
309     bitdb.dbenv.txn_checkpoint(nMinutes ? GetArgUInt("-dblogsize", 100)*1024 : 0, nMinutes, 0);
310
311     {
312         LOCK(bitdb.cs_db);
313         --bitdb.mapFileUseCount[strFile];
314     }
315 }
316
317 void CDBEnv::CloseDb(const string& strFile)
318 {
319     {
320         LOCK(cs_db);
321         if (mapDb[strFile] != NULL)
322         {
323             // Close the database handle
324             Db* pdb = mapDb[strFile];
325             pdb->close(0);
326             delete pdb;
327             mapDb[strFile] = NULL;
328         }
329     }
330 }
331
332 bool CDBEnv::RemoveDb(const string& strFile)
333 {
334     this->CloseDb(strFile);
335
336     LOCK(cs_db);
337     int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
338     return (rc == 0);
339 }
340
341 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
342 {
343     while (!fShutdown)
344     {
345         {
346             LOCK(bitdb.cs_db);
347             if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
348             {
349                 // Flush log data to the dat file
350                 bitdb.CloseDb(strFile);
351                 bitdb.CheckpointLSN(strFile);
352                 bitdb.mapFileUseCount.erase(strFile);
353
354                 bool fSuccess = true;
355                 printf("Rewriting %s...\n", strFile.c_str());
356                 string strFileRes = strFile + ".rewrite";
357                 { // surround usage of db with extra {}
358                     CDB db(strFile.c_str(), "r");
359                     Db* pdbCopy = new Db(&bitdb.dbenv, 0);
360
361                     int ret = pdbCopy->open(NULL,                 // Txn pointer
362                                             strFileRes.c_str(),   // Filename
363                                             "main",    // Logical db name
364                                             DB_BTREE,  // Database type
365                                             DB_CREATE,    // Flags
366                                             0);
367                     if (ret > 0)
368                     {
369                         printf("Cannot create database file %s\n", strFileRes.c_str());
370                         fSuccess = false;
371                     }
372
373                     Dbc* pcursor = db.GetCursor();
374                     if (pcursor) {
375                         while (fSuccess)
376                         {
377                             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
378                             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
379                             int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
380                             if (ret == DB_NOTFOUND)
381                             {
382                                 pcursor->close();
383                                 break;
384                             }
385                             else if (ret != 0)
386                             {
387                                 pcursor->close();
388                                 fSuccess = false;
389                                 break;
390                             }
391
392                             if (pszSkip != NULL)
393                             {
394                                 size_t pszSkipLen = strlen(pszSkip);
395                                 if (strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), pszSkipLen)) == 0)
396                                     continue;
397                             }
398
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                     }
412                     if (fSuccess)
413                     {
414                         db.Close();
415                         bitdb.CloseDb(strFile);
416                         if (pdbCopy->close(0))
417                             fSuccess = false;
418                         delete pdbCopy;
419                     }
420                 }
421                 if (fSuccess)
422                 {
423                     Db dbA(&bitdb.dbenv, 0);
424                     if (dbA.remove(strFile.c_str(), NULL, 0))
425                         fSuccess = false;
426                     Db dbB(&bitdb.dbenv, 0);
427                     if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
428                         fSuccess = false;
429                 }
430                 if (!fSuccess)
431                     printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
432                 return fSuccess;
433             }
434         }
435         Sleep(100);
436     }
437     return false;
438 }
439
440
441 void CDBEnv::Flush(bool fShutdown)
442 {
443     int64_t nStart = GetTimeMillis();
444     // Flush log data to the actual data file
445     //  on all files that are not in use
446     printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
447     if (!fDbEnvInit)
448         return;
449     {
450         LOCK(cs_db);
451         map<string, int>::iterator mi = mapFileUseCount.begin();
452         while (mi != mapFileUseCount.end())
453         {
454             string strFile = (*mi).first;
455             int nRefCount = (*mi).second;
456             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
457             if (nRefCount == 0)
458             {
459                 // Move log data to the dat file
460                 CloseDb(strFile);
461                 printf("%s checkpoint\n", strFile.c_str());
462                 dbenv.txn_checkpoint(0, 0, 0);
463                 if (!IsChainFile(strFile) || fDetachDB) {
464                     printf("%s detach\n", strFile.c_str());
465                     if (!fMockDb)
466                         dbenv.lsn_reset(strFile.c_str(), 0);
467                 }
468                 printf("%s closed\n", strFile.c_str());
469                 mapFileUseCount.erase(mi++);
470             }
471             else
472                 mi++;
473         }
474         printf("DBFlush(%s)%s ended %15" PRId64 "ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
475         if (fShutdown)
476         {
477             char** listp;
478             if (mapFileUseCount.empty())
479             {
480                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
481                 Close();
482             }
483         }
484     }
485 }
486
487
488 //
489 // CAddrDB
490 //
491
492
493 CAddrDB::CAddrDB()
494 {
495     pathAddr = GetDataDir() / "peers.dat";
496 }
497
498 bool CAddrDB::Write(const CAddrMan& addr)
499 {
500     // Generate random temporary filename
501     unsigned short randv = 0;
502     RAND_bytes((unsigned char *)&randv, sizeof(randv));
503     std::string tmpfn = strprintf("peers.dat.%04x", randv);
504
505     // serialize addresses, checksum data up to that point, then append csum
506     CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
507     ssPeers << FLATDATA(pchMessageStart);
508     ssPeers << addr;
509     uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
510     ssPeers << hash;
511
512     // open temp output file, and associate with CAutoFile
513     boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
514     FILE *file = fopen(pathTmp.string().c_str(), "wb");
515     CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
516     if (!fileout)
517         return error("CAddrman::Write() : open failed");
518
519     // Write and commit header, data
520     try {
521         fileout << ssPeers;
522     }
523     catch (const std::exception&) {
524         return error("CAddrman::Write() : I/O error");
525     }
526     FileCommit(fileout);
527     fileout.fclose();
528
529     // replace existing peers.dat, if any, with new peers.dat.XXXX
530     if (!RenameOver(pathTmp, pathAddr))
531         return error("CAddrman::Write() : Rename-into-place failed");
532
533     return true;
534 }
535
536 bool CAddrDB::Read(CAddrMan& addr)
537 {
538     // open input file, and associate with CAutoFile
539     FILE *file = fopen(pathAddr.string().c_str(), "rb");
540     CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
541     if (!filein)
542         return error("CAddrman::Read() : open failed");
543
544     // use file size to size memory buffer
545     int fileSize = GetFilesize(filein);
546     int dataSize = fileSize - sizeof(uint256);
547     //Don't try to resize to a negative number if file is small
548     if ( dataSize < 0 ) dataSize = 0;
549     vector<unsigned char> vchData;
550     vchData.resize(dataSize);
551     uint256 hashIn;
552
553     // read data and checksum from file
554     try {
555         filein.read((char *)&vchData[0], dataSize);
556         filein >> hashIn;
557     }
558     catch (const std::exception&) {
559         return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
560     }
561     filein.fclose();
562
563     CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
564
565     // verify stored checksum matches input data
566     uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
567     if (hashIn != hashTmp)
568         return error("CAddrman::Read() : checksum mismatch; data corrupted");
569
570     unsigned char pchMsgTmp[4];
571     try {
572         // de-serialize file header (pchMessageStart magic number) and
573         ssPeers >> FLATDATA(pchMsgTmp);
574
575         // verify the network matches ours
576         if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
577             return error("CAddrman::Read() : invalid network magic number");
578
579         // de-serialize address data into one CAddrMan object
580         ssPeers >> addr;
581     }
582     catch (const std::exception&) {
583         return error("CAddrman::Read() : I/O error or stream data corrupted");
584     }
585
586     return true;
587 }
588