Update CMakeLists.txt - play with openssl
[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 #include "hash.h"
11
12 #include <boost/filesystem.hpp>
13 #include <boost/filesystem/fstream.hpp>
14
15 #ifndef WIN32
16 #include "sys/stat.h"
17 #endif
18
19 using namespace std;
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() : 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 void CDB::Close()
284 {
285     if (!pdb)
286         return;
287     if (activeTxn)
288         activeTxn->abort();
289     activeTxn = NULL;
290     pdb = NULL;
291
292     // Flush database activity from memory pool to disk log
293     unsigned int nMinutes = 0;
294     if (fReadOnly)
295         nMinutes = 1;
296
297     bitdb.dbenv.txn_checkpoint(nMinutes ? GetArgUInt("-dblogsize", 100)*1024 : 0, nMinutes, 0);
298
299     {
300         LOCK(bitdb.cs_db);
301         --bitdb.mapFileUseCount[strFile];
302     }
303 }
304
305 void CDBEnv::CloseDb(const string& strFile)
306 {
307     {
308         LOCK(cs_db);
309         if (mapDb[strFile] != NULL)
310         {
311             // Close the database handle
312             Db* pdb = mapDb[strFile];
313             pdb->close(0);
314             delete pdb;
315             mapDb[strFile] = NULL;
316         }
317     }
318 }
319
320 bool CDBEnv::RemoveDb(const string& strFile)
321 {
322     this->CloseDb(strFile);
323
324     LOCK(cs_db);
325     int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
326     return (rc == 0);
327 }
328
329 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
330 {
331     while (!fShutdown)
332     {
333         {
334             LOCK(bitdb.cs_db);
335             if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
336             {
337                 // Flush log data to the dat file
338                 bitdb.CloseDb(strFile);
339                 bitdb.CheckpointLSN(strFile);
340                 bitdb.mapFileUseCount.erase(strFile);
341
342                 bool fSuccess = true;
343                 printf("Rewriting %s...\n", strFile.c_str());
344                 string strFileRes = strFile + ".rewrite";
345                 { // surround usage of db with extra {}
346                     CDB db(strFile.c_str(), "r");
347                     Db* pdbCopy = new Db(&bitdb.dbenv, 0);
348
349                     int ret = pdbCopy->open(NULL,                 // Txn pointer
350                                             strFileRes.c_str(),   // Filename
351                                             "main",    // Logical db name
352                                             DB_BTREE,  // Database type
353                                             DB_CREATE,    // Flags
354                                             0);
355                     if (ret > 0)
356                     {
357                         printf("Cannot create database file %s\n", strFileRes.c_str());
358                         fSuccess = false;
359                     }
360
361                     Dbc* pcursor = db.GetCursor();
362                     if (pcursor) {
363                         while (fSuccess)
364                         {
365                             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
366                             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
367                             int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
368                             if (ret == DB_NOTFOUND)
369                             {
370                                 pcursor->close();
371                                 break;
372                             }
373                             else if (ret != 0)
374                             {
375                                 pcursor->close();
376                                 fSuccess = false;
377                                 break;
378                             }
379
380                             if (pszSkip != NULL)
381                             {
382                                 size_t pszSkipLen = strlen(pszSkip);
383                                 if (strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), pszSkipLen)) == 0)
384                                     continue;
385                             }
386
387                             if (strncmp(&ssKey[0], "\x07version", 8) == 0)
388                             {
389                                 // Update version:
390                                 ssValue.clear();
391                                 ssValue << CLIENT_VERSION;
392                             }
393                             Dbt datKey(&ssKey[0], ssKey.size());
394                             Dbt datValue(&ssValue[0], ssValue.size());
395                             int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
396                             if (ret2 > 0)
397                                 fSuccess = false;
398                         }
399                     }
400                     if (fSuccess)
401                     {
402                         db.Close();
403                         bitdb.CloseDb(strFile);
404                         if (pdbCopy->close(0))
405                             fSuccess = false;
406                         delete pdbCopy;
407                     }
408                 }
409                 if (fSuccess)
410                 {
411                     Db dbA(&bitdb.dbenv, 0);
412                     if (dbA.remove(strFile.c_str(), NULL, 0))
413                         fSuccess = false;
414                     Db dbB(&bitdb.dbenv, 0);
415                     if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
416                         fSuccess = false;
417                 }
418                 if (!fSuccess)
419                     printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
420                 return fSuccess;
421             }
422         }
423         Sleep(100);
424     }
425     return false;
426 }
427
428
429 void CDBEnv::Flush(bool fShutdown)
430 {
431     int64_t nStart = GetTimeMillis();
432     // Flush log data to the actual data file
433     //  on all files that are not in use
434     printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
435     if (!fDbEnvInit)
436         return;
437     {
438         LOCK(cs_db);
439         map<string, int>::iterator mi = mapFileUseCount.begin();
440         while (mi != mapFileUseCount.end())
441         {
442             string strFile = (*mi).first;
443             int nRefCount = (*mi).second;
444             printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
445             if (nRefCount == 0)
446             {
447                 // Move log data to the dat file
448                 CloseDb(strFile);
449                 printf("%s checkpoint\n", strFile.c_str());
450                 dbenv.txn_checkpoint(0, 0, 0);
451                 printf("%s detach\n", strFile.c_str());
452                 if (!fMockDb)
453                     dbenv.lsn_reset(strFile.c_str(), 0);
454                 printf("%s closed\n", strFile.c_str());
455                 mapFileUseCount.erase(mi++);
456             }
457             else
458                 mi++;
459         }
460         printf("DBFlush(%s)%s ended %15" PRId64 "ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
461         if (fShutdown)
462         {
463             char** listp;
464             if (mapFileUseCount.empty())
465             {
466                 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
467                 Close();
468             }
469         }
470     }
471 }
472
473
474 //
475 // CAddrDB
476 //
477
478 unsigned char CAddrDB::pchMessageStart[4] = {};
479
480 CAddrDB::CAddrDB()
481 {
482     pathAddr = GetDataDir() / "peers.dat";
483 }
484
485 bool CAddrDB::Write(const CAddrMan& addr)
486 {
487     // Generate random temporary filename
488     unsigned short randv = 0;
489     GetRandBytes((unsigned char *)&randv, sizeof(randv));
490     std::string tmpfn = strprintf("peers.dat.%04x", randv);
491
492     // serialize addresses, checksum data up to that point, then append csum
493     CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
494     ssPeers << FLATDATA(CAddrDB::pchMessageStart);
495     ssPeers << addr;
496     uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
497     ssPeers << hash;
498
499     // open temp output file, and associate with CAutoFile
500     boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
501     FILE *file = fopen(pathTmp.string().c_str(), "wb");
502     CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
503     if (!fileout)
504         return error("CAddrman::Write() : open failed");
505
506     // Write and commit header, data
507     try {
508         fileout << ssPeers;
509     }
510     catch (const std::exception&) {
511         return error("CAddrman::Write() : I/O error");
512     }
513     FileCommit(fileout);
514     fileout.fclose();
515
516     // replace existing peers.dat, if any, with new peers.dat.XXXX
517     if (!RenameOver(pathTmp, pathAddr))
518         return error("CAddrman::Write() : Rename-into-place failed");
519
520     return true;
521 }
522
523 bool CAddrDB::Read(CAddrMan& addr)
524 {
525     // open input file, and associate with CAutoFile
526     FILE *file = fopen(pathAddr.string().c_str(), "rb");
527     CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
528     if (!filein)
529         return error("CAddrman::Read() : open failed");
530
531     // use file size to size memory buffer
532     int fileSize = GetFilesize(filein);
533     int dataSize = fileSize - sizeof(uint256);
534     //Don't try to resize to a negative number if file is small
535     if ( dataSize < 0 ) dataSize = 0;
536     vector<unsigned char> vchData;
537     vchData.resize(dataSize);
538     uint256 hashIn;
539
540     // read data and checksum from file
541     try {
542         filein.read((char *)&vchData[0], dataSize);
543         filein >> hashIn;
544     }
545     catch (const std::exception&) {
546         return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
547     }
548     filein.fclose();
549
550     CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
551
552     // verify stored checksum matches input data
553     uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
554     if (hashIn != hashTmp)
555         return error("CAddrman::Read() : checksum mismatch; data corrupted");
556
557     unsigned char pchMsgTmp[4];
558     try {
559         // de-serialize file header (CAddrDB::pchMessageStart magic number) and
560         ssPeers >> FLATDATA(pchMsgTmp);
561
562         // verify the network matches ours
563         if (memcmp(pchMsgTmp, CAddrDB::pchMessageStart, sizeof(pchMsgTmp)))
564             return error("CAddrman::Read() : invalid network magic number");
565
566         // de-serialize address data into one CAddrMan object
567         ssPeers >> addr;
568     }
569     catch (const std::exception&) {
570         return error("CAddrman::Read() : I/O error or stream data corrupted");
571     }
572
573     return true;
574 }
575