From 9e9869d0fe9c5cb38273a557e28b1922356d0e11 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 10 Nov 2011 21:29:23 +0100 Subject: Resilvering --- src/db.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/db.h | 5 +++- src/wallet.cpp | 10 +++++++ 3 files changed, 104 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/db.cpp b/src/db.cpp index 7de1f8df9a..f083c77736 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -165,6 +165,90 @@ void static CloseDb(const string& strFile) } } +bool Resilver(const string& strFile) +{ + 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("Resilvering %s...\n", strFile.c_str()); + string strFileRes = strFile + ".resilver"; + 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) + break; + else if (ret != 0) + { + pcursor->close(); + fSuccess = false; + break; + } + 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* pdb = mapDb[strFile]; + if (pdb->close(0)) + fSuccess = false; + if (pdbCopy->close(0)) + fSuccess = false; + delete pdb; + delete pdbCopy; + mapDb[strFile] = NULL; + } + 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("Resilvering 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 @@ -656,6 +740,8 @@ int CWalletDB::LoadWallet(CWallet* pwallet) pwallet->vchDefaultKey.clear(); int nFileVersion = 0; vector vWalletUpgrade; + bool fIsResilvered = false; + bool fIsEncrypted = false; // Modify defaults #ifndef WIN32 @@ -781,6 +867,7 @@ int CWalletDB::LoadWallet(CWallet* pwallet) ssValue >> vchPrivKey; if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) return DB_CORRUPT; + fIsEncrypted = true; } else if (strType == "defaultkey") { @@ -814,6 +901,7 @@ 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") @@ -851,6 +939,8 @@ 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 73ea1902cc..75748ace01 100644 --- a/src/db.h +++ b/src/db.h @@ -32,7 +32,7 @@ extern DbEnv dbenv; extern void DBFlush(bool fShutdown); void ThreadFlushWalletDB(void* parg); bool BackupWallet(const CWallet& wallet, const std::string& strDest); - +extern bool Resilver(const std::string& strFile); @@ -257,6 +257,8 @@ public: { return Write(std::string("version"), nVersion); } + + friend bool Resilver(const std::string&); }; @@ -349,6 +351,7 @@ enum DBErrors DB_CORRUPT, DB_TOO_NEW, DB_LOAD_FAIL, + DB_NEED_RESILVER }; class CWalletDB : public CDB diff --git a/src/wallet.cpp b/src/wallet.cpp index 64ee5c3b8c..3ed880f600 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -189,6 +189,9 @@ bool CWallet::EncryptWallet(const string& strWalletPassphrase) Lock(); } + if (Resilver(strWalletFile)) + CWalletDB(strWalletFile, "r+").WriteSetting("fIsResilvered", true); + return true; } @@ -1142,6 +1145,13 @@ int CWallet::LoadWallet(bool& fFirstRunRet) return false; fFirstRunRet = false; int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + if (nLoadWalletRet == DB_NEED_RESILVER) + { + if (Resilver(strWalletFile)) + CWalletDB(strWalletFile, "r+").WriteSetting("fIsResilvered", true); + nLoadWalletRet = DB_LOAD_OK; + } + if (nLoadWalletRet != DB_LOAD_OK) return nLoadWalletRet; fFirstRunRet = vchDefaultKey.empty(); -- cgit v1.2.3 From d764d9161e548c7fbf835b70eb9d5c75caa28ff5 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 10 Nov 2011 21:12:46 -0500 Subject: Obsolete keypool and make sure database removes log files on shutdown. --- src/bitcoinrpc.cpp | 11 ++++++++- src/db.cpp | 70 +++++++++++++++++++++++++++++++++++++++--------------- src/db.h | 7 +++--- src/init.cpp | 14 +++++++---- src/serialize.h | 2 +- src/wallet.cpp | 21 ++++++++++------ 6 files changed, 89 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 24864030c1..31ef725d79 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1557,6 +1557,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 QT_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()); @@ -1576,7 +1581,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/db.cpp b/src/db.cpp index f083c77736..f163ac949a 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); + } } } } @@ -740,7 +774,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) pwallet->vchDefaultKey.clear(); int nFileVersion = 0; vector vWalletUpgrade; - bool fIsResilvered = false; bool fIsEncrypted = false; // Modify defaults @@ -901,7 +934,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") @@ -929,8 +961,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]) @@ -939,9 +974,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 75748ace01..98af4be9a0 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 d6e153285e..c91c098e03 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -44,8 +44,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; @@ -55,9 +55,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; @@ -362,6 +362,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/serialize.h b/src/serialize.h index beb87f1d04..53867e939a 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -60,7 +60,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 50000; +static const int VERSION = 50001; static const char* pszSubVer = ""; static const bool VERSION_IS_BETA = true; diff --git a/src/wallet.cpp b/src/wallet.cpp index 3ed880f600..e3ca7d297d 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -187,10 +187,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; } @@ -1145,11 +1147,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) -- cgit v1.2.3 From 4585f7e2c1ee7e090dc0cb59cf1ff82139393818 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 11 Nov 2011 22:20:57 +0100 Subject: add message about restarting bitcoin after encrypting wallet succesfully --- src/qt/askpassphrasedialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 89cdf43ba4..a574ef925b 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -101,7 +101,8 @@ void AskPassphraseDialog::accept() if(model->setWalletEncrypted(true, newpass1)) { QMessageBox::warning(this, tr("Wallet encrypted"), - tr("Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.")); + tr("Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.")); + QApplication::quit(); } else { -- cgit v1.2.3