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