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