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