aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/developer-notes.md2
-rw-r--r--src/fs.cpp16
-rw-r--r--src/fs.h2
-rw-r--r--src/init.cpp9
-rw-r--r--src/qt/askpassphrasedialog.cpp5
-rw-r--r--src/rpc/protocol.cpp2
-rw-r--r--src/wallet/db.cpp43
-rw-r--r--src/wallet/db.h4
-rw-r--r--src/wallet/rpcwallet.cpp7
-rw-r--r--src/wallet/wallet.cpp7
-rwxr-xr-xtest/functional/example_test.py4
-rwxr-xr-xtest/functional/interface_zmq.py15
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py6
-rwxr-xr-xtest/functional/test_framework/test_node.py10
-rwxr-xr-xtest/functional/wallet_bumpfee.py3
-rwxr-xr-xtest/functional/wallet_dump.py9
-rwxr-xr-xtest/functional/wallet_encryption.py3
-rwxr-xr-xtest/functional/wallet_import_rescan.py2
-rwxr-xr-xtest/functional/wallet_keypool.py4
-rwxr-xr-xtest/lint/lint-spelling.sh3
20 files changed, 104 insertions, 52 deletions
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index da8384c537..7c9106a6c1 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -512,7 +512,7 @@ Strings and formatting
- *Rationale*: These functions do overflow checking, and avoid pesky locale issues.
- Avoid using locale dependent functions if possible. You can use the provided
- [`lint-locale-dependence.sh`](/contrib/devtools/lint-locale-dependence.sh)
+ [`lint-locale-dependence.sh`](/test/lint/lint-locale-dependence.sh)
to check for accidental use of locale dependent functions.
- *Rationale*: Unnecessary locale dependence can cause bugs that are very tricky to isolate and fix.
diff --git a/src/fs.cpp b/src/fs.cpp
index a34a88216a..df79b5e3df 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -97,4 +97,20 @@ bool FileLock::TryLock()
}
#endif
+std::string get_filesystem_error_message(const fs::filesystem_error& e)
+{
+#ifndef WIN32
+ return e.what();
+#else
+ // Convert from Multi Byte to utf-16
+ std::string mb_string(e.what());
+ int size = MultiByteToWideChar(CP_ACP, 0, mb_string.c_str(), mb_string.size(), nullptr, 0);
+
+ std::wstring utf16_string(size, L'\0');
+ MultiByteToWideChar(CP_ACP, 0, mb_string.c_str(), mb_string.size(), &*utf16_string.begin(), size);
+ // Convert from utf-16 to utf-8
+ return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(utf16_string);
+#endif
+}
+
} // fsbridge
diff --git a/src/fs.h b/src/fs.h
index 5a28d9a81c..5492bdd4ec 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -38,6 +38,8 @@ namespace fsbridge {
void* hFile = (void*)-1; // INVALID_HANDLE_VALUE
#endif
};
+
+ std::string get_filesystem_error_message(const fs::filesystem_error& e);
};
#endif // BITCOIN_FS_H
diff --git a/src/init.cpp b/src/init.cpp
index 45689f7ecd..93568c97a9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -426,7 +426,14 @@ void SetupServerArgs()
#endif
gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is (0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
+ "level 0 reads the blocks from disk, "
+ "level 1 verifies block validity, "
+ "level 2 verifies undo data, "
+ "level 3 checks disconnection of tip blocks, "
+ "and level 4 tries to reconnect the blocks, "
+ "each level includes the checks of the previous levels "
+ "(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST);
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index 673f63a19b..a0270daf99 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -123,16 +123,15 @@ void AskPassphraseDialog::accept()
{
QMessageBox::warning(this, tr("Wallet encrypted"),
"<qt>" +
- 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)) +
+ "your bitcoins from being stolen by malware infecting your computer.") +
"<br><br><b>" +
tr("IMPORTANT: Any previous backups you have 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.") +
"</b></qt>");
- QApplication::quit();
}
else
{
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index dab63ab263..55bebb5662 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -128,7 +128,7 @@ void DeleteAuthCookie()
try {
fs::remove(GetAuthCookieFile());
} catch (const fs::filesystem_error& e) {
- LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
+ LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
}
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index dfd60ae5eb..a7bf89c572 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -556,6 +556,7 @@ void BerkeleyBatch::Close()
LOCK(cs_db);
--env->mapFileUseCount[strFile];
}
+ env->m_db_in_use.notify_all();
}
void BerkeleyEnvironment::CloseDb(const std::string& strFile)
@@ -572,6 +573,32 @@ void BerkeleyEnvironment::CloseDb(const std::string& strFile)
}
}
+void BerkeleyEnvironment::ReloadDbEnv()
+{
+ // Make sure that no Db's are in use
+ AssertLockNotHeld(cs_db);
+ std::unique_lock<CCriticalSection> lock(cs_db);
+ m_db_in_use.wait(lock, [this](){
+ for (auto& count : mapFileUseCount) {
+ if (count.second > 0) return false;
+ }
+ return true;
+ });
+
+ std::vector<std::string> 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
+ Flush(true); // This will flush and close the environment
+ Reset();
+ Open(true);
+}
+
bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
{
if (database.IsDummy()) {
@@ -697,7 +724,6 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
if (!fMockDb) {
fs::remove_all(fs::path(strPath) / "database");
}
- g_dbenvs.erase(strPath);
}
}
}
@@ -783,7 +809,7 @@ bool BerkeleyDatabase::Backup(const std::string& strDest)
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;
} catch (const fs::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
+ LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
return false;
}
}
@@ -796,6 +822,17 @@ void BerkeleyDatabase::Flush(bool shutdown)
{
if (!IsDummy()) {
env->Flush(shutdown);
- if (shutdown) env = nullptr;
+ 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/db.h b/src/wallet/db.h
index b078edab7b..467ed13b45 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -38,6 +38,7 @@ public:
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
std::map<std::string, Db*> mapDb;
+ std::condition_variable_any m_db_in_use;
BerkeleyEnvironment(const fs::path& env_directory);
~BerkeleyEnvironment();
@@ -75,6 +76,7 @@ public:
void CheckpointLSN(const std::string& strFile);
void CloseDb(const std::string& strFile);
+ void ReloadDbEnv();
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
{
@@ -145,6 +147,8 @@ public:
void IncrementUpdateCounter();
+ void ReloadDbEnv();
+
std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen;
unsigned int nLastFlushed;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 15f3e5947e..1a2dff9a96 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2121,7 +2121,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
"will require the passphrase to be set prior the making these calls.\n"
"Use the walletpassphrase call for this, and then walletlock 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 long.\n"
"\nExamples:\n"
@@ -2159,11 +2158,7 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "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 was generated (if you are using HD). You need to make a new backup.";
+ 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.";
}
static UniValue lockunspent(const JSONRPCRequest& request)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 593035eb78..7f7a88e986 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -722,6 +722,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// 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);
@@ -3849,7 +3854,7 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
return false;
}
} catch (const fs::filesystem_error& e) {
- error_string = strprintf("Error loading wallet %s. %s", wallet_file, e.what());
+ error_string = strprintf("Error loading wallet %s. %s", wallet_file, fsbridge::get_filesystem_error_message(e));
return false;
}
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 4c9d60b337..937a525401 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -115,7 +115,7 @@ class ExampleTest(BitcoinTestFramework):
# sync_all() should not include node2, since we're not expecting it to
# sync.
connect_nodes(self.nodes[0], 1)
- self.sync_all([self.nodes[0:1]])
+ self.sync_all([self.nodes[0:2]])
# Use setup_nodes() to customize the node start behaviour (for example if
# you don't want to start all nodes at the start of the test).
@@ -139,7 +139,7 @@ class ExampleTest(BitcoinTestFramework):
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
- self.sync_all([self.nodes[0:1]])
+ self.sync_all([self.nodes[0:2]])
# Notice above how we called an RPC by calling a method with the same
# name on the node object. Notice also how we used a keyword argument
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index c853ba7e3f..1c518eab75 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -44,12 +44,6 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_wallet()
def setup_nodes(self):
- # Import keys
- self.add_nodes(self.num_nodes)
- self.start_nodes()
- super().import_deterministic_coinbase_privkeys()
- self.stop_nodes()
-
import zmq
# Initialize ZMQ context and socket.
@@ -69,12 +63,13 @@ class ZMQTest (BitcoinTestFramework):
self.rawblock = ZMQSubscriber(socket, b"rawblock")
self.rawtx = ZMQSubscriber(socket, b"rawtx")
- self.nodes[0].extra_args = ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]]
+ self.extra_args = [
+ ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]],
+ [],
+ ]
+ self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
- def import_deterministic_coinbase_privkeys(self):
- pass
-
def run_test(self):
try:
self._zmq_test()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index daa890ab15..0c61e9ab62 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -478,10 +478,8 @@ class RawTransactionsTest(BitcoinTestFramework):
############################################################
# 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
index 908dda94c5..c267f7f24f 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -100,7 +100,7 @@ class TestNode():
def get_deterministic_priv_key(self):
"""Return a deterministic priv key in base58, that only depends on the node's index"""
PRIV_KEYS = [
- # adress , privkey
+ # address , privkey
('mjTkW3DjgyZck4KbiRusZsqTgaYTxdSz6z', 'cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW'),
('msX6jQXvxiNhx3Q62PKeLPrhrqZQdSimTg', 'cUxsWyKyZ9MAQTaAhUQWJmBbSvHMwSmuv59KgxQV7oZQU3PXN3KE'),
('mnonCMyH9TmAsSj3M59DsbH8H63U3RKoFP', 'cTrh7dkEAeJd6b3MRX9bZK8eRmNqVCMH3LSUkE3dSFDyzjU38QxK'),
@@ -305,14 +305,6 @@ class TestNode():
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 add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs):
"""Add a p2p connection to the node.
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 2ea72896bb..67ee00871d 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -42,8 +42,7 @@ class BumpFeeTest(BitcoinTestFramework):
def run_test(self):
# Encrypt wallet for test_locked_wallet_fails test
- self.nodes[1].node_encrypt_wallet(WALLET_PASSPHRASE)
- self.start_node(1)
+ self.nodes[1].encryptwallet(WALLET_PASSPHRASE)
self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
connect_nodes_bi(self.nodes, 0, 1)
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index db731b2a34..b1db1e4ab9 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -34,7 +34,11 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
# key = key_date_label[0]
date = key_date_label[1]
keytype = key_date_label[2]
- if not len(comment) or date.startswith('1970'):
+
+ imported_key = date == '1970-01-01T00:00:01Z'
+ if imported_key:
+ # Imported keys have multiple addresses, no label (keypath) and timestamp
+ # Skip them
continue
addr_keypath = comment.split(" addr=")[1]
@@ -128,8 +132,7 @@ class WalletDumpTest(BitcoinTestFramework):
assert_equal(witness_addr_ret, witness_addr) # p2sh-p2wsh address added to the first key
#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
index d8c27b09d9..ab9ebed8d4 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -33,8 +33,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_equal(len(privkey), 52)
# Encrypt the wallet
- self.nodes[0].node_encrypt_wallet(passphrase)
- self.start_node(0)
+ self.nodes[0].encryptwallet(passphrase)
# Test that the wallet is encrypted
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index fab415987e..f043a544fc 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -120,7 +120,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=extra_args)
- # Import keys
+ # Import keys with pruning disabled
self.start_nodes(extra_args=[[]] * self.num_nodes)
super().import_deterministic_coinbase_privkeys()
self.stop_nodes()
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index acc336e4d5..51afa0cb1a 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -25,9 +25,7 @@ class KeyPoolTest(BitcoinTestFramework):
assert(addr_before_encrypting_data['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)
diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh
index 5d672698a7..ebeafd7d58 100755
--- a/test/lint/lint-spelling.sh
+++ b/test/lint/lint-spelling.sh
@@ -9,7 +9,10 @@
export LC_ALL=C
+EXIT_CODE=0
IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt
if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then
echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}"
+ EXIT_CODE=1
fi
+exit ${EXIT_CODE}