Headers cleanup
[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 "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() : fDetachDB(false), 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 static bool IsChainFile(std::string strFile)
283 {
284     if (strFile == "blkindex.dat")
285         return true;
286
287     return false;
288 }
289
290 void CDB::Close()
291 {
292     if (!pdb)
293         return;
294     if (activeTxn)
295         activeTxn->abort();
296     activeTxn = NULL;
297     pdb = NULL;
298
299     // Flush database activity from memory pool to disk log
300     unsigned int nMinutes = 0;
301     if (fReadOnly)
302         nMinutes = 1;
303     if (IsChainFile(strFile))
304         nMinutes = 2;
305     if (IsChainFile(strFile) && IsInitialBlockDownload())
306         nMinutes = 5;
307
308     bitdb.dbenv.txn_checkpoint(nMinutes ? GetArgUInt("-dblogsize", 100)*1024 : 0, nMinutes, 0);
309
310     {
311         LOCK(bitdb.cs_db);
312         --bitdb.mapFileUseCount[strFile];
313     }
314 }
315
316 void CDBEnv::CloseDb(const string& strFile)
317 {
318     {
319         LOCK(cs_db);
320         if (mapDb[strFile] != NULL)
321         {
322             // Close the database handle
323             Db* pdb = mapDb[strFile];
324             pdb->close(0);
325             delete pdb;
326             mapDb[strFile] = NULL;
327         }
328     }
329 }
330
331 bool CDBEnv::RemoveDb(const string& strFile)
332 {
333     this->CloseDb(strFile);
334
335     LOCK(cs_db);
336     int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
337     return (rc == 0);
338 }
339
340 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
341 {
342     while (!fShutdown)
343     {
344         {
345             LOCK(bitdb.cs_db);
346             if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
347             {
348                 // Flush log data to the dat file
349                 bitdb.CloseDb(strFile);
350                 bitdb.CheckpointLSN(strFile);
351                 bitdb.mapFileUseCount.erase(strFile);
352
353                 bool fSuccess = true;
354                 printf("Rewriting %s...\n", strFile.c_str());
355                 string strFileRes = strFile + ".rewrite";
356                 { // surround usage of db with extra {}
357                     CDB db(strFile.c_str(), "r");
358                     Db* pdbCopy = new Db(&bitdb.dbenv, 0);
359
360                     int ret = pdbCopy->open(NULL,                 // Txn pointer
361                                             strFileRes.c_str(),   // Filename
362                                             "main",    // Logical db name
363                                             DB_BTREE,  // Database type
364                                             DB_CREATE,    // Flags
365                                             0);
366                     if (ret > 0)
367                     {
368                         printf("Cannot create database file %s\n", strFileRes.c_str());
369                         fSuccess = false;
370                     }
371
372                     Dbc* pcursor = db.GetCursor();
373                     if (pcursor) {
374                         while (fSuccess)
375                         {
376                             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
377                             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
378                             int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
379                             if (ret == DB_NOTFOUND)
380                             {
381                                 pcursor->close();
382                                 break;
383                             }
384                             else if (ret != 0)
385                             {
386                                 pcursor->close();
387                                 fSuccess = false;
388                                 break;
389                             }
390
391                             if (pszSkip != NULL)
392                             {
393                                 size_t pszSkipLen = strlen(pszSkip);
394                                 if (strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), pszSkipLen)) == 0)
395                                     continue;
396                             }
397
398                             if (strncmp(&ssKey[0], "\x07version", 8) == 0)
399                             {
400                                 // Update version:
401                                 ssValue.clear();
402                                 ssValue << CLIENT_VERSION;
403                             }
404                             Dbt datKey(&ssKey[0], ssKey.size());
405                             Dbt datValue(&ssValue[0], ssValue.size());
406                             int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
407                             if (ret2 > 0)
408                                 fSuccess = false;
409                         }
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_t 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" PRId64 "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 // CAddrDB
489 //
490
491
492 CAddrDB::CAddrDB()
493 {
494     pathAddr = GetDataDir() / "peers.dat";
495 }
496
497 bool CAddrDB::Write(const CAddrMan& addr)
498 {
499     // Generate random temporary filename
500     unsigned short randv = 0;
501     GetRandBytes((unsigned char *)&randv, sizeof(randv));
502     std::string tmpfn = strprintf("peers.dat.%04x", randv);
503
504     // serialize addresses, checksum data up to that point, then append csum
505     CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
506     ssPeers << FLATDATA(pchMessageStart);
507     ssPeers << addr;
508     uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
509     ssPeers << hash;
510
511     // open temp output file, and associate with CAutoFile
512     boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
513     FILE *file = fopen(pathTmp.string().c_str(), "wb");
514     CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
515     if (!fileout)
516         return error("CAddrman::Write() : open failed");
517
518     // Write and commit header, data
519     try {
520         fileout << ssPeers;
521     }
522     catch (const std::exception&) {
523         return error("CAddrman::Write() : I/O error");
524     }
525     FileCommit(fileout);
526     fileout.fclose();
527
528     // replace existing peers.dat, if any, with new peers.dat.XXXX
529     if (!RenameOver(pathTmp, pathAddr))
530         return error("CAddrman::Write() : Rename-into-place failed");
531
532     return true;
533 }
534
535 bool CAddrDB::Read(CAddrMan& addr)
536 {
537     // open input file, and associate with CAutoFile
538     FILE *file = fopen(pathAddr.string().c_str(), "rb");
539     CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
540     if (!filein)
541         return error("CAddrman::Read() : open failed");
542
543     // use file size to size memory buffer
544     int fileSize = GetFilesize(filein);
545     int dataSize = fileSize - sizeof(uint256);
546     //Don't try to resize to a negative number if file is small
547     if ( dataSize < 0 ) dataSize = 0;
548     vector<unsigned char> vchData;
549     vchData.resize(dataSize);
550     uint256 hashIn;
551
552     // read data and checksum from file
553     try {
554         filein.read((char *)&vchData[0], dataSize);
555         filein >> hashIn;
556     }
557     catch (const std::exception&) {
558         return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
559     }
560     filein.fclose();
561
562     CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
563
564     // verify stored checksum matches input data
565     uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
566     if (hashIn != hashTmp)
567         return error("CAddrman::Read() : checksum mismatch; data corrupted");
568
569     unsigned char pchMsgTmp[4];
570     try {
571         // de-serialize file header (pchMessageStart magic number) and
572         ssPeers >> FLATDATA(pchMsgTmp);
573
574         // verify the network matches ours
575         if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
576             return error("CAddrman::Read() : invalid network magic number");
577
578         // de-serialize address data into one CAddrMan object
579         ssPeers >> addr;
580     }
581     catch (const std::exception&) {
582         return error("CAddrman::Read() : I/O error or stream data corrupted");
583     }
584
585     return true;
586 }
587