diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -123,24 +123,21 @@ QMessageBox::warning( this, tr("Wallet encrypted"), "" + - tr("%1 will close now to finish the encryption " - "process. " + tr("Your wallet is now encrypted. " "Remember that encrypting your wallet " - "cannot fully protect " - "your bitcoins from being stolen by malware " - "infecting your computer.") - .arg(tr(PACKAGE_NAME)) + + "cannot fully protect your bitcoins from " + "being stolen by malware infecting your " + "computer.") + "

" + tr("IMPORTANT: Any previous backups you have " - "made of your wallet file " - "should be replaced with the newly " - "generated, encrypted wallet file. " + "made of your wallet file should be " + "replaced with the newly generated, " + "encrypted wallet file. " "For security reasons, previous backups of " - "the unencrypted wallet file " - "will become useless as soon as you start " - "using the new, encrypted wallet.") + + "the unencrypted wallet file will become " + "useless as soon as you start using the " + "new, encrypted wallet.") + "
"); - QApplication::quit(); } else { QMessageBox::critical( this, tr("Wallet encryption failed"), diff --git a/src/wallet/db.h b/src/wallet/db.h --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -38,6 +38,7 @@ std::unique_ptr dbenv; std::map mapFileUseCount; std::map mapDb; + std::condition_variable_any m_db_in_use; BerkeleyEnvironment(const fs::path &env_directory); ~BerkeleyEnvironment(); @@ -79,6 +80,7 @@ void CheckpointLSN(const std::string &strFile); void CloseDb(const std::string &strFile); + void ReloadDbEnv(); DbTxn *TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { DbTxn *ptxn = nullptr; @@ -155,6 +157,8 @@ void IncrementUpdateCounter(); + void ReloadDbEnv(); + std::atomic nUpdateCounter; unsigned int nLastSeen; unsigned int nLastFlushed; diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -608,8 +608,11 @@ Flush(); } - LOCK(cs_db); - --env->mapFileUseCount[strFile]; + { + LOCK(cs_db); + --env->mapFileUseCount[strFile]; + } + env->m_db_in_use.notify_all(); } void BerkeleyEnvironment::CloseDb(const std::string &strFile) { @@ -623,6 +626,34 @@ } } +void BerkeleyEnvironment::ReloadDbEnv() { + // Make sure that no Db's are in use + AssertLockNotHeld(cs_db); + std::unique_lock lock(cs_db); + m_db_in_use.wait(lock, [this]() { + for (auto &count : mapFileUseCount) { + if (count.second > 0) { + return false; + } + } + return true; + }); + + std::vector filenames; + for (auto it : mapDb) { + filenames.push_back(it.first); + } + // Close the individual Db's + for (const std::string &filename : filenames) { + CloseDb(filename); + } + // Reset the environment + // This will flush and close the environment + Flush(true); + Reset(); + Open(true); +} + bool BerkeleyBatch::Rewrite(BerkeleyDatabase &database, const char *pszSkip) { if (database.IsDummy()) { return true; @@ -779,7 +810,6 @@ if (!fMockDb) { fs::remove_all(fs::path(strPath) / "database"); } - g_dbenvs.erase(strPath); } } } @@ -876,7 +906,15 @@ if (!IsDummy()) { env->Flush(shutdown); if (shutdown) { + LOCK(cs_db); + g_dbenvs.erase(env->Directory().string()); env = nullptr; } } } + +void BerkeleyDatabase::ReloadDbEnv() { + if (!IsDummy()) { + env->ReloadDbEnv(); + } +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3262,7 +3262,6 @@ "call.\n" "If the wallet is already encrypted, use the " "walletpassphrasechange call.\n" - "Note that this will shutdown the server.\n" "\nArguments:\n" "1. \"passphrase\" (string) The pass phrase to encrypt the " "wallet with. It must be at least 1 character, but should be " @@ -3309,12 +3308,7 @@ "Error: Failed to encrypt the wallet."); } - // 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: - StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with " - "encrypted wallet. The keypool has been flushed and a new HD seed " + return "wallet encrypted; The keypool has been flushed and a new HD seed " "was generated (if you are using HD). You need to make a new " "backup."; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -800,6 +800,11 @@ // keep bits of the unencrypted private key in slack space in the // database file. database->Rewrite(); + + // 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: + database->ReloadDbEnv(); } NotifyStatusChanged(this); diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -483,10 +483,8 @@ # # locked wallet test - self.stop_node(0) - self.nodes[1].node_encrypt_wallet("test") - self.stop_node(2) - self.stop_node(3) + self.nodes[1].encryptwallet("test") + self.stop_nodes() self.start_nodes() # This test is not meant to test fee estimation and we'd like diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -350,14 +350,6 @@ assert_msg = "bitcoind should have exited with expected error " + expected_msg self._raise_assertion_error(assert_msg) - def node_encrypt_wallet(self, passphrase): - """"Encrypts the wallet. - - This causes bitcoind to shutdown, so this method takes - care of cleaning up resources.""" - self.encryptwallet(passphrase) - self.wait_until_stopped() - def relay_fee(self, cached=True): if not self.relay_fee_cache or not cached: self.relay_fee_cache = self.getnetworkinfo()["relayfee"] diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -117,8 +117,7 @@ assert_equal(found_addr_rsv, 90 * 2) # encrypt wallet, restart, unlock and dump - self.nodes[0].node_encrypt_wallet('test') - self.start_node(0) + self.nodes[0].encryptwallet('test') self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: self.nodes[0].keypoolrefill() diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.py @@ -32,8 +32,7 @@ assert_equal(len(privkey), 52) # Encrypt the wallet - self.nodes[0].node_encrypt_wallet(passphrase) - self.start_node(0) + self.nodes[0].encryptwallet(passphrase) # Check the encrypted wallet is marked as locked on initialization assert_equal(self.nodes[0].getwalletinfo()['unlocked_until'], 0) diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -25,9 +25,7 @@ 'hdseedid'] == wallet_info_old['hdseedid'] # Encrypt wallet and wait to terminate - nodes[0].node_encrypt_wallet('test') - # Restart node 0 - self.start_node(0) + nodes[0].encryptwallet('test') # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].getaddressinfo(addr)