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.
10 #include "ui_interface.h"
12 #include <boost/filesystem.hpp>
13 #include <boost/filesystem/fstream.hpp>
20 using namespace boost;
23 unsigned int nWalletDBUpdated;
24 extern bool fUseMemoryLog;
33 void CDBEnv::EnvShutdown()
39 int ret = dbenv.close(0);
41 printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
43 DbEnv(0).remove(strPath.c_str(), 0);
46 CDBEnv::CDBEnv() : fDetachDB(false), fDbEnvInit(false), fMockDb(false), dbenv(DB_CXX_NO_EXCEPTIONS) { }
58 bool CDBEnv::Open(boost::filesystem::path pathEnv_)
67 filesystem::path pathDataDir = pathEnv;
68 strPath = pathDataDir.string();
69 filesystem::path pathLogDir = pathDataDir / "database";
70 filesystem::create_directory(pathLogDir);
71 filesystem::path pathErrorFile = pathDataDir / "db.log";
72 printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
74 unsigned int nEnvFlags = 0;
75 if (GetBoolArg("-privdb", true))
76 nEnvFlags |= DB_PRIVATE;
78 int nDbCache = GetArgInt("-dbcache", 25);
79 dbenv.set_lg_dir(pathLogDir.string().c_str());
80 dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
81 dbenv.set_lg_bsize(1048576);
82 dbenv.set_lg_max(10485760);
84 // Bugfix: Bump lk_max_locks default to 537000, to safely handle reorgs with up to 5 blocks reversed
85 // dbenv.set_lk_max_locks(10000);
86 dbenv.set_lk_max_locks(537000);
88 dbenv.set_lk_max_objects(10000);
89 dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
90 dbenv.set_flags(DB_AUTO_COMMIT, 1);
91 dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
92 #ifdef DB_LOG_AUTO_REMOVE
93 dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
95 int ret = dbenv.open(strPath.c_str(),
106 return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
112 // Check that the number of locks is sufficient (to prevent chain fork possibility, read http://bitcoin.org/may15 for more info)
114 if (!dbenv.get_lk_max_locks(&nMaxLocks))
116 int nBlocks, nDeepReorg;
117 std::string strMessage;
119 nBlocks = nMaxLocks / 48768;
120 nDeepReorg = (nBlocks - 1) / 2;
122 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);
126 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);
128 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);
130 strMiscWarning = strMessage;
131 printf("*** %s\n", strMessage.c_str());
139 void CDBEnv::MakeMock()
142 throw runtime_error("CDBEnv::MakeMock(): already initialized");
145 throw runtime_error("CDBEnv::MakeMock(): during shutdown");
147 printf("CDBEnv::MakeMock()\n");
149 dbenv.set_cachesize(1, 0, 1);
150 dbenv.set_lg_bsize(10485760*4);
151 dbenv.set_lg_max(10485760);
152 dbenv.set_lk_max_locks(10000);
153 dbenv.set_lk_max_objects(10000);
154 dbenv.set_flags(DB_AUTO_COMMIT, 1);
155 #ifdef DB_LOG_IN_MEMORY
156 dbenv.log_set_config(DB_LOG_IN_MEMORY, fUseMemoryLog ? 1 : 0);
158 int ret = dbenv.open(NULL,
168 throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
174 CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
177 assert(mapFileUseCount.count(strFile) == 0);
180 int result = db.verify(strFile.c_str(), NULL, NULL, 0);
183 else if (recoverFunc == NULL)
187 bool fRecovered = (*recoverFunc)(*this, strFile);
188 return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
191 bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
192 std::vector<CDBEnv::KeyValPair >& vResult)
195 assert(mapFileUseCount.count(strFile) == 0);
197 u_int32_t flags = DB_SALVAGE;
198 if (fAggressive) flags |= DB_AGGRESSIVE;
200 stringstream strDump;
203 int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
206 printf("ERROR: db salvage failed\n");
210 // Format of bdb dump is ascii lines:
219 while (!strDump.eof() && strLine != "HEADER=END")
220 getline(strDump, strLine); // Skip past header
222 std::string keyHex, valueHex;
223 while (!strDump.eof() && keyHex != "DATA=END")
225 getline(strDump, keyHex);
226 if (keyHex != "DATA=END")
228 getline(strDump, valueHex);
229 vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
233 return (result == 0);
237 void CDBEnv::CheckpointLSN(std::string strFile)
239 dbenv.txn_checkpoint(0, 0, 0);
242 dbenv.lsn_reset(strFile.c_str(), 0);
246 CDB::CDB(const char *pszFile, const char* pszMode) :
247 pdb(NULL), activeTxn(NULL)
253 fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
254 bool fCreate = strchr(pszMode, 'c') != NULL;
255 unsigned int nFlags = DB_THREAD;
261 if (!bitdb.Open(GetDataDir()))
262 throw runtime_error("env open failed");
265 ++bitdb.mapFileUseCount[strFile];
266 pdb = bitdb.mapDb[strFile];
269 pdb = new Db(&bitdb.dbenv, 0);
271 bool fMockDb = bitdb.IsMock();
274 DbMpoolFile*mpf = pdb->get_mpf();
275 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
277 throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
280 ret = pdb->open(NULL, // Txn pointer
281 fMockDb ? NULL : pszFile, // Filename
282 "main", // Logical db name
283 DB_BTREE, // Database type
291 --bitdb.mapFileUseCount[strFile];
293 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
296 if (fCreate && !Exists(string("version")))
298 bool fTmp = fReadOnly;
300 WriteVersion(CLIENT_VERSION);
304 bitdb.mapDb[strFile] = pdb;
309 static bool IsChainFile(std::string strFile)
311 if (strFile == "blkindex.dat")
326 // Flush database activity from memory pool to disk log
327 unsigned int nMinutes = 0;
330 if (IsChainFile(strFile))
332 if (IsChainFile(strFile) && IsInitialBlockDownload())
335 bitdb.dbenv.txn_checkpoint(nMinutes ? GetArgUInt("-dblogsize", 100)*1024 : 0, nMinutes, 0);
339 --bitdb.mapFileUseCount[strFile];
343 void CDBEnv::CloseDb(const string& strFile)
347 if (mapDb[strFile] != NULL)
349 // Close the database handle
350 Db* pdb = mapDb[strFile];
353 mapDb[strFile] = NULL;
358 bool CDBEnv::RemoveDb(const string& strFile)
360 this->CloseDb(strFile);
363 int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
367 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
373 if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
375 // Flush log data to the dat file
376 bitdb.CloseDb(strFile);
377 bitdb.CheckpointLSN(strFile);
378 bitdb.mapFileUseCount.erase(strFile);
380 bool fSuccess = true;
381 printf("Rewriting %s...\n", strFile.c_str());
382 string strFileRes = strFile + ".rewrite";
383 { // surround usage of db with extra {}
384 CDB db(strFile.c_str(), "r");
385 Db* pdbCopy = new Db(&bitdb.dbenv, 0);
387 int ret = pdbCopy->open(NULL, // Txn pointer
388 strFileRes.c_str(), // Filename
389 "main", // Logical db name
390 DB_BTREE, // Database type
395 printf("Cannot create database file %s\n", strFileRes.c_str());
399 Dbc* pcursor = db.GetCursor();
403 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
404 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
405 int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
406 if (ret == DB_NOTFOUND)
420 size_t pszSkipLen = strlen(pszSkip);
421 if (strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), pszSkipLen)) == 0)
425 if (strncmp(&ssKey[0], "\x07version", 8) == 0)
429 ssValue << CLIENT_VERSION;
431 Dbt datKey(&ssKey[0], ssKey.size());
432 Dbt datValue(&ssValue[0], ssValue.size());
433 int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
441 bitdb.CloseDb(strFile);
442 if (pdbCopy->close(0))
449 Db dbA(&bitdb.dbenv, 0);
450 if (dbA.remove(strFile.c_str(), NULL, 0))
452 Db dbB(&bitdb.dbenv, 0);
453 if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
457 printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
467 void CDBEnv::Flush(bool fShutdown)
469 int64_t nStart = GetTimeMillis();
470 // Flush log data to the actual data file
471 // on all files that are not in use
472 printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
477 auto mi = mapFileUseCount.begin();
478 while (mi != mapFileUseCount.end())
480 string strFile = (*mi).first;
481 int nRefCount = (*mi).second;
482 printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
485 // Move log data to the dat file
487 printf("%s checkpoint\n", strFile.c_str());
488 dbenv.txn_checkpoint(0, 0, 0);
489 if (!IsChainFile(strFile) || fDetachDB) {
490 printf("%s detach\n", strFile.c_str());
492 dbenv.lsn_reset(strFile.c_str(), 0);
494 printf("%s closed\n", strFile.c_str());
495 mapFileUseCount.erase(mi++);
500 printf("DBFlush(%s)%s ended %15" PRId64 "ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
504 if (mapFileUseCount.empty())
506 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
521 pathAddr = GetDataDir() / "peers.dat";
524 bool CAddrDB::Write(const CAddrMan& addr)
526 // Generate random temporary filename
527 unsigned short randv = 0;
528 RAND_bytes((unsigned char *)&randv, sizeof(randv));
529 std::string tmpfn = strprintf("peers.dat.%04x", randv);
531 // serialize addresses, checksum data up to that point, then append csum
532 CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
533 ssPeers << FLATDATA(pchMessageStart);
535 uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
538 // open temp output file, and associate with CAutoFile
539 boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
540 FILE *file = fopen(pathTmp.string().c_str(), "wb");
541 CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
543 return error("CAddrman::Write() : open failed");
545 // Write and commit header, data
549 catch (const std::exception&) {
550 return error("CAddrman::Write() : I/O error");
555 // replace existing peers.dat, if any, with new peers.dat.XXXX
556 if (!RenameOver(pathTmp, pathAddr))
557 return error("CAddrman::Write() : Rename-into-place failed");
562 bool CAddrDB::Read(CAddrMan& addr)
564 // open input file, and associate with CAutoFile
565 FILE *file = fopen(pathAddr.string().c_str(), "rb");
566 CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
568 return error("CAddrman::Read() : open failed");
570 // use file size to size memory buffer
571 int fileSize = GetFilesize(filein);
572 int dataSize = fileSize - sizeof(uint256);
573 //Don't try to resize to a negative number if file is small
574 if ( dataSize < 0 ) dataSize = 0;
575 vector<unsigned char> vchData;
576 vchData.resize(dataSize);
579 // read data and checksum from file
581 filein.read((char *)&vchData[0], dataSize);
584 catch (const std::exception&) {
585 return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
589 CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
591 // verify stored checksum matches input data
592 uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
593 if (hashIn != hashTmp)
594 return error("CAddrman::Read() : checksum mismatch; data corrupted");
596 unsigned char pchMsgTmp[4];
598 // de-serialize file header (pchMessageStart magic number) and
599 ssPeers >> FLATDATA(pchMsgTmp);
601 // verify the network matches ours
602 if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
603 return error("CAddrman::Read() : invalid network magic number");
605 // de-serialize address data into one CAddrMan object
608 catch (const std::exception&) {
609 return error("CAddrman::Read() : I/O error or stream data corrupted");