X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fdb.cpp;h=68d317106da206bf1e22595da99a022b7b2aff31;hb=58ac600b2c94f12309fc5e18933891590dc1eb4c;hp=071231c5ddcd241a7a2479f401fca8dcc039efe4;hpb=b17be7e14b4a62b7935c75fe7e7645e736fd68d2;p=novacoin.git diff --git a/src/db.cpp b/src/db.cpp index 071231c..68d3171 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -1,10 +1,16 @@ // 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 +#include -void ThreadFlushWalletDB(void* parg); +using namespace std; +using namespace boost; unsigned int nWalletDBUpdated; @@ -22,6 +28,23 @@ DbEnv dbenv(0); static map mapFileUseCount; static map mapDb; +static void EnvShutdown() +{ + if (!fDbEnvInit) + return; + + fDbEnvInit = false; + try + { + dbenv.close(0); + } + catch (const DbException& e) + { + printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno()); + } + DbEnv(0).remove(GetDataDir().c_str(), 0); +} + class CDBInit { public: @@ -30,11 +53,7 @@ public: } ~CDBInit() { - if (fDbEnvInit) - { - dbenv.close(0); - fDbEnvInit = false; - } + EnvShutdown(); } } instance_of_cdbinit; @@ -144,7 +163,7 @@ void CDB::Close() --mapFileUseCount[strFile]; } -void CloseDb(const string& strFile) +void static CloseDb(const string& strFile) { CRITICAL_BLOCK(cs_db) { @@ -159,6 +178,101 @@ void CloseDb(const string& strFile) } } +bool CDB::Rewrite(const string& strFile, const char* pszSkip) +{ + while (!fShutdown) + { + CRITICAL_BLOCK(cs_db) + { + if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) + { + // Flush log data to the dat file + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(strFile); + + bool fSuccess = true; + printf("Rewriting %s...\n", strFile.c_str()); + string strFileRes = strFile + ".rewrite"; + { // surround usage of db with extra {} + CDB db(strFile.c_str(), "r"); + Db* pdbCopy = new Db(&dbenv, 0); + + int ret = pdbCopy->open(NULL, // Txn pointer + strFileRes.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", strFileRes.c_str()); + fSuccess = false; + } + + Dbc* pcursor = db.GetCursor(); + if (pcursor) + while (fSuccess) + { + CDataStream ssKey; + CDataStream ssValue; + int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + if (ret == DB_NOTFOUND) + { + pcursor->close(); + break; + } + else if (ret != 0) + { + pcursor->close(); + fSuccess = false; + break; + } + if (pszSkip && + strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) + continue; + if (strncmp(&ssKey[0], "\x07version", 8) == 0) + { + // Update version: + ssValue.clear(); + ssValue << VERSION; + } + Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datValue(&ssValue[0], ssValue.size()); + int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + if (fSuccess) + { + db.Close(); + CloseDb(strFile); + if (pdbCopy->close(0)) + fSuccess = false; + delete pdbCopy; + } + } + if (fSuccess) + { + Db dbA(&dbenv, 0); + if (dbA.remove(strFile.c_str(), NULL, 0)) + fSuccess = false; + Db dbB(&dbenv, 0); + if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + fSuccess = false; + } + if (!fSuccess) + printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); + return fSuccess; + } + } + Sleep(100); + } + return false; +} + + void DBFlush(bool fShutdown) { // Flush log data to the actual data file @@ -190,9 +304,10 @@ void DBFlush(bool fShutdown) { char** listp; if (mapFileUseCount.empty()) + { dbenv.log_archive(&listp, DB_ARCH_REMOVE); - dbenv.close(0); - fDbEnvInit = false; + EnvShutdown(); + } } } } @@ -353,7 +468,7 @@ bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) return Write(string("bnBestInvalidWork"), bnBestInvalidWork); } -CBlockIndex* InsertBlockIndex(uint256 hash) +CBlockIndex static * InsertBlockIndex(uint256 hash) { if (hash == 0) return NULL; @@ -434,13 +549,13 @@ bool CTxDB::LoadBlockIndex() // Calculate bnChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); - foreach(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { CBlockIndex* pindex = item.second; vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); - foreach(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) { CBlockIndex* pindex = item.second; pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); @@ -523,7 +638,7 @@ bool CAddrDB::LoadAddresses() char psz[1000]; while (fgets(psz, sizeof(psz), filein)) { - CAddress addr(psz, NODE_NETWORK); + CAddress addr(psz, false, NODE_NETWORK); addr.nTime = 0; // so it won't relay unless successfully connected if (addr.IsValid()) AddAddress(addr); @@ -578,8 +693,19 @@ bool LoadAddresses() // CWalletDB // -static set setKeyPool; -static CCriticalSection cs_setKeyPool; +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) { @@ -594,7 +720,7 @@ bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) { - return Write(make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); + return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); } int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) @@ -603,7 +729,7 @@ int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) ListAccountCreditDebit(strAccount, entries); int64 nCreditDebit = 0; - foreach (const CAccountingEntry& entry, entries) + BOOST_FOREACH (const CAccountingEntry& entry, entries) nCreditDebit += entry.nCreditDebit; return nCreditDebit; @@ -611,8 +737,6 @@ int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) { - int64 nCreditDebit = 0; - bool fAllAccounts = (strAccount == "*"); Dbc* pcursor = GetCursor(); @@ -624,7 +748,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, listvchDefaultKey.clear(); int nFileVersion = 0; vector vWalletUpgrade; + bool fIsEncrypted = false; // Modify defaults #ifndef __WXMSW__ @@ -668,13 +793,22 @@ bool CWalletDB::LoadWallet() #endif //// todo: shouldn't we catch exceptions and try to recover and continue? - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(pwallet->cs_wallet) { + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > VERSION) + return DB_TOO_NEW; + } + // Get cursor Dbc* pcursor = GetCursor(); if (!pcursor) - return false; + { + printf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } loop { @@ -685,7 +819,10 @@ bool CWalletDB::LoadWallet() if (ret == DB_NOTFOUND) break; else if (ret != 0) - return false; + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } // Unserialize // Taking advantage of the fact that pair serialization @@ -696,14 +833,15 @@ bool CWalletDB::LoadWallet() { string strAddress; ssKey >> strAddress; - ssValue >> mapAddressBook[strAddress]; + ssValue >> pwallet->mapAddressBook[strAddress]; } else if (strType == "tx") { uint256 hash; ssKey >> hash; - CWalletTx& wtx = mapWallet[hash]; + CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; + wtx.pwallet = pwallet; if (wtx.GetHash() != hash) printf("Error in wallet.dat, hash mismatch\n"); @@ -729,7 +867,7 @@ bool CWalletDB::LoadWallet() //// debug print //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12I64d %s %s %s\n", + //printf(" %12"PRI64d" %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(), @@ -748,24 +886,82 @@ bool CWalletDB::LoadWallet() { vector vchPubKey; ssKey >> vchPubKey; - CWalletKey wkey; + CKey key; if (strType == "key") - ssValue >> wkey.vchPrivKey; + { + CPrivKey pkey; + ssValue >> pkey; + 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; - - mapKeys[vchPubKey] = wkey.vchPrivKey; - mapPubKeys[Hash160(vchPubKey)] = 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 >> vchDefaultKey; + ssValue >> pwallet->vchDefaultKey; } else if (strType == "pool") { int64 nIndex; ssKey >> nIndex; - setKeyPool.insert(nIndex); + pwallet->setKeyPool.insert(nIndex); } else if (strType == "version") { @@ -783,7 +979,6 @@ bool CWalletDB::LoadWallet() if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; #endif if (strKey == "nTransactionFee") ssValue >> nTransactionFee; - if (strKey == "addrIncoming") ssValue >> addrIncoming; if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; @@ -796,13 +991,12 @@ bool CWalletDB::LoadWallet() pcursor->close(); } - foreach(uint256 hash, vWalletUpgrade) - WriteTx(hash, mapWallet[hash]); + BOOST_FOREACH(uint256 hash, vWalletUpgrade) + WriteTx(hash, pwallet->mapWallet[hash]); printf("nFileVersion = %d\n", nFileVersion); printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); printf("nTransactionFee = %"PRI64d"\n", nTransactionFee); - printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); printf("fMinimizeToTray = %d\n", fMinimizeToTray); printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); printf("fUseProxy = %d\n", fUseProxy); @@ -811,8 +1005,11 @@ bool CWalletDB::LoadWallet() printf("fUseUPnP = %d\n", fUseUPnP); - // Upgrade - if (nFileVersion < VERSION) + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) + return DB_NEED_REWRITE; + + if (nFileVersion < VERSION) // Update { // Get rid of old debug.log file in current directory if (nFileVersion <= 105 && !pszSetDataDir[0]) @@ -821,41 +1018,12 @@ bool CWalletDB::LoadWallet() WriteVersion(VERSION); } - - return true; -} - -bool LoadWallet(bool& fFirstRunRet) -{ - fFirstRunRet = false; - if (!CWalletDB("cr+").LoadWallet()) - return false; - fFirstRunRet = vchDefaultKey.empty(); - - if (mapKeys.count(vchDefaultKey)) - { - // Set keyUser - keyUser.SetPubKey(vchDefaultKey); - keyUser.SetPrivKey(mapKeys[vchDefaultKey]); - } - else - { - // Create new keyUser and set as default key - RandAddSeedPerfmon(); - keyUser.MakeNewKey(); - if (!AddKey(keyUser)) - return false; - if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "")) - return false; - CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); - } - - CreateThread(ThreadFlushWalletDB, NULL); - return true; + return DB_LOAD_OK; } void ThreadFlushWalletDB(void* parg) { + const string& strFile = ((const string*)parg)[0]; static bool fOneThread; if (fOneThread) return; @@ -891,7 +1059,6 @@ void ThreadFlushWalletDB(void* parg) if (nRefCount == 0 && !fShutdown) { - string strFile = "wallet.dat"; map::iterator mi = mapFileUseCount.find(strFile); if (mi != mapFileUseCount.end()) { @@ -914,26 +1081,27 @@ void ThreadFlushWalletDB(void* parg) } } -void BackupWallet(const string& strDest) +bool BackupWallet(const CWallet& wallet, const string& strDest) { + if (!wallet.fFileBacked) + return false; while (!fShutdown) { CRITICAL_BLOCK(cs_db) { - const string strFile = "wallet.dat"; - if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) + if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file - CloseDb(strFile); + CloseDb(wallet.strWalletFile); dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(strFile.c_str(), 0); - mapFileUseCount.erase(strFile); + dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0); + mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet.dat - filesystem::path pathSrc(GetDataDir() + "/" + strFile); + filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile); filesystem::path pathDest(strDest); if (filesystem::is_directory(pathDest)) - pathDest = pathDest / strFile; + pathDest = pathDest / wallet.strWalletFile; #if BOOST_VERSION >= 104000 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); #else @@ -941,83 +1109,10 @@ void BackupWallet(const string& strDest) #endif printf("copied wallet.dat to %s\n", pathDest.string().c_str()); - return; + return true; } } Sleep(100); } -} - - -void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) -{ - nIndex = -1; - keypool.vchPubKey.clear(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_setKeyPool) - { - // Top up key pool - int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); - while (setKeyPool.size() < nTargetSize+1) - { - int64 nEnd = 1; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; - if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) - throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); - setKeyPool.insert(nEnd); - printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); - } - - // Get the oldest key - assert(!setKeyPool.empty()); - nIndex = *(setKeyPool.begin()); - setKeyPool.erase(setKeyPool.begin()); - if (!Read(make_pair(string("pool"), nIndex), keypool)) - throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!mapKeys.count(keypool.vchPubKey)) - throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); - assert(!keypool.vchPubKey.empty()); - printf("keypool reserve %"PRI64d"\n", nIndex); - } -} - -void CWalletDB::KeepKey(int64 nIndex) -{ - // Remove from key pool - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - Erase(make_pair(string("pool"), nIndex)); - } - printf("keypool keep %"PRI64d"\n", nIndex); -} - -void CWalletDB::ReturnKey(int64 nIndex) -{ - // Return to key pool - CRITICAL_BLOCK(cs_setKeyPool) - setKeyPool.insert(nIndex); - printf("keypool return %"PRI64d"\n", nIndex); -} - -vector GetKeyFromKeyPool() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.KeepKey(nIndex); - return keypool.vchPubKey; -} - -int64 GetOldestKeyPoolTime() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.ReturnKey(nIndex); - return keypool.nTime; + return false; }