X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fdb.cpp;h=c67a34c39e865cae267b8ce4fca145bd252955e2;hb=23e7583a8c9a0dcee9cbbf3be8bfc453298773f0;hp=b86a56e41640a2ab47fd7c6654341bde5ce969d9;hpb=e88ed65a39eb86ffd4d0720852d7e5f3d021f792;p=novacoin.git diff --git a/src/db.cpp b/src/db.cpp index b86a56e..c67a34c 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -1,21 +1,24 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" #include "db.h" -#include "net.h" +#include "util.h" +#include "main.h" #include #include #include +#ifndef WIN32 +#include "sys/stat.h" +#endif + using namespace std; using namespace boost; unsigned int nWalletDBUpdated; -uint64 nAccountingEntryNumber = 0; @@ -23,10 +26,11 @@ uint64 nAccountingEntryNumber = 0; // CDB // -static CCriticalSection cs_db; +CCriticalSection cs_db; static bool fDbEnvInit = false; +bool fDetachDB = false; DbEnv dbenv(0); -static map mapFileUseCount; +map mapFileUseCount; static map mapDb; static void EnvShutdown() @@ -43,7 +47,7 @@ static void EnvShutdown() { printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno()); } - DbEnv(0).remove(GetDataDir().c_str(), 0); + DbEnv(0).remove(GetDataDir().string().c_str(), 0); } class CDBInit @@ -60,7 +64,7 @@ public: instance_of_cdbinit; -CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) +CDB::CDB(const char *pszFile, const char* pszMode) : pdb(NULL) { int ret; if (pszFile == NULL) @@ -72,29 +76,30 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) if (fCreate) nFlags |= DB_CREATE; - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!fDbEnvInit) { if (fShutdown) return; - string strDataDir = GetDataDir(); - string strLogDir = strDataDir + "/database"; - filesystem::create_directory(strLogDir.c_str()); - string strErrorFile = strDataDir + "/db.log"; - printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str()); + filesystem::path pathDataDir = GetDataDir(); + filesystem::path pathLogDir = pathDataDir / "database"; + filesystem::create_directory(pathLogDir); + filesystem::path pathErrorFile = pathDataDir / "db.log"; + printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); int nDbCache = GetArg("-dbcache", 25); - dbenv.set_lg_dir(strLogDir.c_str()); + dbenv.set_lg_dir(pathLogDir.string().c_str()); dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); dbenv.set_lg_max(10485760); dbenv.set_lk_max_locks(10000); dbenv.set_lk_max_objects(10000); - dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug + dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug + dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); - ret = dbenv.open(strDataDir.c_str(), + ret = dbenv.open(pathDataDir.string().c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -126,8 +131,10 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) { delete pdb; pdb = NULL; - CRITICAL_BLOCK(cs_db) + { + LOCK(cs_db); --mapFileUseCount[strFile]; + } strFile = ""; throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); } @@ -160,19 +167,23 @@ void CDB::Close() nMinutes = 1; if (strFile == "addr.dat") nMinutes = 2; + if (strFile == "blkindex.dat") + nMinutes = 2; if (strFile == "blkindex.dat" && IsInitialBlockDownload()) nMinutes = 5; dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); - CRITICAL_BLOCK(cs_db) + { + LOCK(cs_db); --mapFileUseCount[strFile]; + } } -void static CloseDb(const string& strFile) +void CloseDb(const string& strFile) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (mapDb[strFile] != NULL) { // Close the database handle @@ -188,8 +199,8 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) { while (!fShutdown) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) { // Flush log data to the dat file @@ -221,8 +232,8 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) if (pcursor) while (fSuccess) { - CDataStream ssKey; - CDataStream ssValue; + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); if (ret == DB_NOTFOUND) { @@ -286,8 +297,8 @@ void DBFlush(bool fShutdown) printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); if (!fDbEnvInit) return; - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { @@ -298,9 +309,13 @@ void DBFlush(bool fShutdown) { // Move log data to the dat file CloseDb(strFile); + printf("%s checkpoint\n", strFile.c_str()); dbenv.txn_checkpoint(0, 0, 0); - printf("%s flush\n", strFile.c_str()); - dbenv.lsn_reset(strFile.c_str(), 0); + if ((strFile != "blkindex.dat" && strFile != "addr.dat") || fDetachDB) { + printf("%s detach\n", strFile.c_str()); + dbenv.lsn_reset(strFile.c_str(), 0); + } + printf("%s closed\n", strFile.c_str()); mapFileUseCount.erase(mi++); } else @@ -378,10 +393,10 @@ bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector& loop { // Read next record - CDataStream ssKey; + CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); - CDataStream ssValue; + CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) @@ -396,9 +411,15 @@ bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector& string strType; uint160 hashItem; CDiskTxPos pos; - ssKey >> strType >> hashItem >> pos; int nItemHeight; - ssValue >> nItemHeight; + + try { + ssKey >> strType >> hashItem >> pos; + ssValue >> nItemHeight; + } + catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); + } // Read transaction if (strType != "owner" || hashItem != hash160) @@ -506,10 +527,10 @@ bool CTxDB::LoadBlockIndex() loop { // Read next record - CDataStream ssKey; + CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("blockindex"), uint256(0)); - CDataStream ssValue; + CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) @@ -518,9 +539,11 @@ bool CTxDB::LoadBlockIndex() return false; // Unserialize + + try { string strType; ssKey >> strType; - if (strType == "blockindex") + if (strType == "blockindex" && !fRequestShutdown) { CDiskBlockIndex diskindex; ssValue >> diskindex; @@ -547,11 +570,18 @@ bool CTxDB::LoadBlockIndex() } else { - break; + break; // if shutdown requested or finished loading block index + } + } // try + catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); } } pcursor->close(); + if (fRequestShutdown) + return true; + // Calculate bnChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); @@ -636,8 +666,9 @@ bool CTxDB::LoadBlockIndex() } } // check level 4: check whether spent txouts were spent within the main chain - int nOutput = 0; + unsigned int nOutput = 0; if (nCheckLevel>3) + { BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) { if (!txpos.IsNull()) @@ -678,9 +709,11 @@ bool CTxDB::LoadBlockIndex() } nOutput++; } + } } // check level 5: check whether all prevouts are marked spent if (nCheckLevel>4) + { BOOST_FOREACH(const CTxIn &txin, tx.vin) { CTxIndex txindex; @@ -691,6 +724,7 @@ bool CTxDB::LoadBlockIndex() pindexFork = pindex->pprev; } } + } } } } @@ -742,8 +776,8 @@ bool CAddrDB::LoadAddresses() loop { // Read next record - CDataStream ssKey; - CDataStream ssValue; + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; @@ -778,419 +812,3 @@ bool LoadAddresses() } - - -// -// CWalletDB -// - -bool CWalletDB::WriteName(const string& strAddress, const string& strName) -{ - nWalletDBUpdated++; - return Write(make_pair(string("name"), strAddress), strName); -} - -bool CWalletDB::EraseName(const string& strAddress) -{ - // This should only be used for sending addresses, never for receiving addresses, - // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdated++; - return Erase(make_pair(string("name"), strAddress)); -} - -bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) -{ - account.SetNull(); - return Read(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) -{ - return Write(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) -{ - return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); -} - -int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) -{ - list entries; - ListAccountCreditDebit(strAccount, entries); - - int64 nCreditDebit = 0; - BOOST_FOREACH (const CAccountingEntry& entry, entries) - nCreditDebit += entry.nCreditDebit; - - return nCreditDebit; -} - -void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) -{ - bool fAllAccounts = (strAccount == "*"); - - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); - unsigned int fFlags = DB_SET_RANGE; - loop - { - // Read next record - CDataStream ssKey; - if (fFlags == DB_SET_RANGE) - ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); - } - - // Unserialize - string strType; - ssKey >> strType; - if (strType != "acentry") - break; - CAccountingEntry acentry; - ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) - break; - - ssValue >> acentry; - entries.push_back(acentry); - } - - pcursor->close(); -} - - -int CWalletDB::LoadWallet(CWallet* pwallet) -{ - pwallet->vchDefaultKey.clear(); - int nFileVersion = 0; - vector vWalletUpgrade; - bool fIsEncrypted = false; - - //// todo: shouldn't we catch exceptions and try to recover and continue? - CRITICAL_BLOCK(pwallet->cs_wallet) - { - int nMinVersion = 0; - if (Read((string)"minversion", nMinVersion)) - { - if (nMinVersion > CLIENT_VERSION) - return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } - - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - { - printf("Error getting wallet database cursor\n"); - return DB_CORRUPT; - } - - loop - { - // Read next record - CDataStream ssKey; - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - printf("Error reading next record from wallet database\n"); - return DB_CORRUPT; - } - - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - string strType; - ssKey >> strType; - if (strType == "name") - { - string strAddress; - ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[strAddress]; - } - else if (strType == "tx") - { - uint256 hash; - ssKey >> hash; - CWalletTx& wtx = pwallet->mapWallet[hash]; - ssValue >> wtx; - wtx.BindWallet(pwallet); - - if (wtx.GetHash() != hash) - printf("Error in wallet.dat, hash mismatch\n"); - - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = 0; - } - vWalletUpgrade.push_back(hash); - } - - //// debug print - //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12I64d %s %s %s\n", - // wtx.vout[0].nValue, - // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), - // wtx.hashBlock.ToString().substr(0,20).c_str(), - // wtx.mapValue["message"].c_str()); - } - else if (strType == "acentry") - { - string strAccount; - ssKey >> strAccount; - uint64 nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; - } - else if (strType == "key" || strType == "wkey") - { - vector vchPubKey; - ssKey >> vchPubKey; - CKey key; - if (strType == "key") - { - CPrivKey pkey; - ssValue >> pkey; - key.SetPubKey(vchPubKey); - key.SetPrivKey(pkey); - if (key.GetPubKey() != vchPubKey) - { - printf("Error reading wallet database: CPrivKey pubkey inconsistency\n"); - return DB_CORRUPT; - } - if (!key.IsValid()) - { - printf("Error reading wallet database: invalid CPrivKey\n"); - return DB_CORRUPT; - } - } - else - { - CWalletKey wkey; - ssValue >> wkey; - key.SetPubKey(vchPubKey); - key.SetPrivKey(wkey.vchPrivKey); - if (key.GetPubKey() != vchPubKey) - { - printf("Error reading wallet database: CWalletKey pubkey inconsistency\n"); - return DB_CORRUPT; - } - if (!key.IsValid()) - { - printf("Error reading wallet database: invalid CWalletKey\n"); - return DB_CORRUPT; - } - } - if (!pwallet->LoadKey(key)) - { - printf("Error reading wallet database: LoadKey failed\n"); - return DB_CORRUPT; - } - } - else if (strType == "mkey") - { - unsigned int nID; - ssKey >> nID; - CMasterKey kMasterKey; - ssValue >> kMasterKey; - if(pwallet->mapMasterKeys.count(nID) != 0) - { - printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID); - return DB_CORRUPT; - } - pwallet->mapMasterKeys[nID] = kMasterKey; - if (pwallet->nMasterKeyMaxID < nID) - pwallet->nMasterKeyMaxID = nID; - } - else if (strType == "ckey") - { - vector vchPubKey; - ssKey >> vchPubKey; - vector vchPrivKey; - ssValue >> vchPrivKey; - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) - { - printf("Error reading wallet database: LoadCryptedKey failed\n"); - return DB_CORRUPT; - } - fIsEncrypted = true; - } - else if (strType == "defaultkey") - { - ssValue >> pwallet->vchDefaultKey; - } - else if (strType == "pool") - { - int64 nIndex; - ssKey >> nIndex; - pwallet->setKeyPool.insert(nIndex); - } - else if (strType == "version") - { - ssValue >> nFileVersion; - if (nFileVersion == 10300) - nFileVersion = 300; - } - else if (strType == "cscript") - { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->LoadCScript(script)) - { - printf("Error reading wallet database: LoadCScript failed\n"); - return DB_CORRUPT; - } - } - } - pcursor->close(); - } - - BOOST_FOREACH(uint256 hash, vWalletUpgrade) - WriteTx(hash, pwallet->mapWallet[hash]); - - printf("nFileVersion = %d\n", nFileVersion); - - - // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) - return DB_NEED_REWRITE; - - if (nFileVersion < CLIENT_VERSION) // Update - { - // Get rid of old debug.log file in current directory - if (nFileVersion <= 105 && !pszSetDataDir[0]) - unlink("debug.log"); - - WriteVersion(CLIENT_VERSION); - } - - return DB_LOAD_OK; -} - -void ThreadFlushWalletDB(void* parg) -{ - const string& strFile = ((const string*)parg)[0]; - static bool fOneThread; - if (fOneThread) - return; - fOneThread = true; - if (!GetBoolArg("-flushwallet", true)) - return; - - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; - int64 nLastWalletUpdate = GetTime(); - while (!fShutdown) - { - Sleep(500); - - if (nLastSeen != nWalletDBUpdated) - { - nLastSeen = nWalletDBUpdated; - nLastWalletUpdate = GetTime(); - } - - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) - { - TRY_CRITICAL_BLOCK(cs_db) - { - // Don't do this if any databases are in use - int nRefCount = 0; - map::iterator mi = mapFileUseCount.begin(); - while (mi != mapFileUseCount.end()) - { - nRefCount += (*mi).second; - mi++; - } - - if (nRefCount == 0 && !fShutdown) - { - map::iterator mi = mapFileUseCount.find(strFile); - if (mi != mapFileUseCount.end()) - { - printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("Flushing wallet.dat\n"); - nLastFlushed = nWalletDBUpdated; - int64 nStart = GetTimeMillis(); - - // Flush wallet.dat so it's self contained - CloseDb(strFile); - dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(strFile.c_str(), 0); - - mapFileUseCount.erase(mi++); - printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); - } - } - } - } - } -} - -bool BackupWallet(const CWallet& wallet, const string& strDest) -{ - if (!wallet.fFileBacked) - return false; - while (!fShutdown) - { - CRITICAL_BLOCK(cs_db) - { - if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) - { - // Flush log data to the dat file - CloseDb(wallet.strWalletFile); - dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0); - mapFileUseCount.erase(wallet.strWalletFile); - - // Copy wallet.dat - filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile); - filesystem::path pathDest(strDest); - if (filesystem::is_directory(pathDest)) - pathDest = pathDest / wallet.strWalletFile; - - try { -#if BOOST_VERSION >= 104000 - filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); -#else - filesystem::copy_file(pathSrc, pathDest); -#endif - printf("copied wallet.dat to %s\n", pathDest.string().c_str()); - return true; - } catch(const filesystem::filesystem_error &e) { - printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); - return false; - } - } - } - Sleep(100); - } - return false; -}