From 2744ea8c1fbc2e1ef6b1824a21fd2a75e2b6dbc4 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 10 Nov 2011 21:12:46 -0500 Subject: [PATCH] Obsolete keypool and make sure database removes log files on shutdown. --- src/db.cpp | 70 ++++++++++++++++++++++++++++++++++++++++--------------- src/db.h | 7 ++--- src/init.cpp | 14 ++++++++--- src/rpc.cpp | 11 ++++++++- src/serialize.h | 2 +- src/wallet.cpp | 21 +++++++++++----- 6 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/db.cpp b/src/db.cpp index d41da68..1a1289f 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -28,6 +28,34 @@ DbEnv dbenv(0); static map mapFileUseCount; static map mapDb; +static void EnvShutdown(bool fRemoveLogFiles) +{ + if (!fDbEnvInit) + return; + + fDbEnvInit = false; + dbenv.close(0); + DbEnv(0).remove(GetDataDir().c_str(), 0); + + if (fRemoveLogFiles) + { + filesystem::path datadir(GetDataDir()); + filesystem::directory_iterator it(datadir / "database"); + while (it != filesystem::directory_iterator()) + { + const filesystem::path& p = it->path(); +#if BOOST_FILESYSTEM_VERSION == 2 + std::string f = p.filename(); +#else + std::string f = p.filename().generic_string(); +#endif + if (f.find("log.") == 0) + filesystem::remove(p); + ++it; + } + } +} + class CDBInit { public: @@ -36,11 +64,7 @@ public: } ~CDBInit() { - if (fDbEnvInit) - { - dbenv.close(0); - fDbEnvInit = false; - } + EnvShutdown(false); } } instance_of_cdbinit; @@ -165,7 +189,7 @@ void static CloseDb(const string& strFile) } } -bool Resilver(const string& strFile) +bool CDB::Rewrite(const string& strFile, const char* pszSkip) { while (!fShutdown) { @@ -180,8 +204,8 @@ bool Resilver(const string& strFile) mapFileUseCount.erase(strFile); bool fSuccess = true; - printf("Resilvering %s...\n", strFile.c_str()); - string strFileRes = strFile + ".resilver"; + printf("Rewriting %s...\n", strFile.c_str()); + string strFileRes = strFile + ".rewrite"; CDB db(strFile.c_str(), "r"); Db* pdbCopy = new Db(&dbenv, 0); @@ -212,6 +236,15 @@ bool Resilver(const string& strFile) 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); @@ -239,7 +272,7 @@ bool Resilver(const string& strFile) fSuccess = false; } if (!fSuccess) - printf("Resilvering of %s FAILED!\n", strFileRes.c_str()); + printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); return fSuccess; } } @@ -249,7 +282,7 @@ bool Resilver(const string& strFile) } -void DBFlush(bool fShutdown) +void DBFlush(bool fShutdown, bool fRemoveLogFiles) { // Flush log data to the actual data file // on all files that are not in use @@ -280,9 +313,10 @@ void DBFlush(bool fShutdown) { char** listp; if (mapFileUseCount.empty()) + { dbenv.log_archive(&listp, DB_ARCH_REMOVE); - dbenv.close(0); - fDbEnvInit = false; + EnvShutdown(fRemoveLogFiles); + } } } } @@ -758,7 +792,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) pwallet->vchDefaultKey.clear(); int nFileVersion = 0; vector vWalletUpgrade; - bool fIsResilvered = false; bool fIsEncrypted = false; // Modify defaults @@ -919,7 +952,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose; if (strKey == "fUseProxy") ssValue >> fUseProxy; if (strKey == "addrProxy") ssValue >> addrProxy; - if (strKey == "fIsResilvered") ssValue >> fIsResilvered; if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP; } else if (strType == "minversion") @@ -947,8 +979,11 @@ int CWalletDB::LoadWallet(CWallet* pwallet) 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]) @@ -957,9 +992,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) WriteVersion(VERSION); } - if (fIsEncrypted && !fIsResilvered) - return DB_NEED_RESILVER; - return DB_LOAD_OK; } diff --git a/src/db.h b/src/db.h index 75748ac..98af4be 100644 --- a/src/db.h +++ b/src/db.h @@ -29,10 +29,9 @@ extern unsigned int nWalletDBUpdated; extern DbEnv dbenv; -extern void DBFlush(bool fShutdown); +extern void DBFlush(bool fShutdown, bool fRemoveLogFiles); void ThreadFlushWalletDB(void* parg); bool BackupWallet(const CWallet& wallet, const std::string& strDest); -extern bool Resilver(const std::string& strFile); @@ -258,7 +257,7 @@ public: return Write(std::string("version"), nVersion); } - friend bool Resilver(const std::string&); + bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); }; @@ -351,7 +350,7 @@ enum DBErrors DB_CORRUPT, DB_TOO_NEW, DB_LOAD_FAIL, - DB_NEED_RESILVER + DB_NEED_REWRITE }; class CWalletDB : public CDB diff --git a/src/init.cpp b/src/init.cpp index dbc2c41..4a149b3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -34,8 +34,8 @@ void Shutdown(void* parg) { static CCriticalSection cs_Shutdown; static bool fTaken; - bool fFirstThread; - CRITICAL_BLOCK(cs_Shutdown) + bool fFirstThread = false; + TRY_CRITICAL_BLOCK(cs_Shutdown) { fFirstThread = !fTaken; fTaken = true; @@ -45,9 +45,9 @@ void Shutdown(void* parg) { fShutdown = true; nTransactionsUpdated++; - DBFlush(false); + DBFlush(false, false); StopNode(); - DBFlush(true); + DBFlush(true, true); boost::filesystem::remove(GetPidFile()); UnregisterWallet(pwalletMain); delete pwalletMain; @@ -396,6 +396,12 @@ bool AppInit2(int argc, char* argv[]) strErrors += _("Error loading wallet.dat: Wallet corrupted \n"); else if (nLoadWalletRet == DB_TOO_NEW) strErrors += _("Error loading wallet.dat: Wallet requires newer version of Bitcoin \n"); + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + strErrors += _("Wallet needed to be rewritten: restart Bitcoin to complete \n"); + wxMessageBox(strErrors, "Bitcoin", wxOK | wxICON_ERROR); + return false; + } else strErrors += _("Error loading wallet.dat \n"); } diff --git a/src/rpc.cpp b/src/rpc.cpp index 885ffd1..a936edb 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -1431,6 +1431,11 @@ Value encryptwallet(const Array& params, bool fHelp) if (pwalletMain->IsCrypted()) throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called."); +#ifdef GUI + // shutting down via RPC while the GUI is running does not work (yet): + throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command"); +#endif + string strWalletPass; strWalletPass.reserve(100); mlock(&strWalletPass[0], strWalletPass.capacity()); @@ -1450,7 +1455,11 @@ Value encryptwallet(const Array& params, bool fHelp) fill(strWalletPass.begin(), strWalletPass.end(), '\0'); munlock(&strWalletPass[0], strWalletPass.capacity()); - return Value::null; + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + CreateThread(Shutdown, NULL); + return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet"; } diff --git a/src/serialize.h b/src/serialize.h index 320ce9d..c531d2a 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -59,7 +59,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 40100; +static const int VERSION = 40101; static const char* pszSubVer = ""; static const bool VERSION_IS_BETA = true; diff --git a/src/wallet.cpp b/src/wallet.cpp index 924700d..c004d18 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -188,10 +188,12 @@ bool CWallet::EncryptWallet(const string& strWalletPassphrase) } Lock(); - } - if (Resilver(strWalletFile)) - CWalletDB(strWalletFile, "r+").WriteSetting("fIsResilvered", true); + // Need to completely rewrite the wallet file; if we don't, bdb might keep + // bits of the unencrypted private key in slack space in the database file. + setKeyPool.clear(); + CDB::Rewrite(strWalletFile, "\x04pool"); + } return true; } @@ -1125,11 +1127,16 @@ int CWallet::LoadWallet(bool& fFirstRunRet) return false; fFirstRunRet = false; int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); - if (nLoadWalletRet == DB_NEED_RESILVER) + if (nLoadWalletRet == DB_NEED_REWRITE) { - if (Resilver(strWalletFile)) - CWalletDB(strWalletFile, "r+").WriteSetting("fIsResilvered", true); - nLoadWalletRet = DB_LOAD_OK; + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + nLoadWalletRet = DB_NEED_REWRITE; } if (nLoadWalletRet != DB_LOAD_OK) -- 1.7.1