aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--ci/test/00_setup_env_native_cxx17.sh13
-rw-r--r--ci/test/00_setup_env_native_qt5.sh2
-rw-r--r--ci/test/00_setup_env_native_valgrind.sh2
-rw-r--r--src/Makefile.test.include21
-rw-r--r--src/bench/ccoins_caching.cpp2
-rw-r--r--src/coins.cpp12
-rw-r--r--src/coins.h10
-rw-r--r--src/dummywallet.cpp5
-rw-r--r--src/init.cpp8
-rw-r--r--src/interfaces/chain.cpp144
-rw-r--r--src/interfaces/chain.h104
-rw-r--r--src/interfaces/node.cpp8
-rw-r--r--src/interfaces/node.h7
-rw-r--r--src/interfaces/wallet.cpp46
-rw-r--r--src/interfaces/wallet.h10
-rw-r--r--src/primitives/transaction.h2
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/askpassphrasedialog.cpp3
-rw-r--r--src/qt/bitcoingui.cpp2
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/editaddressdialog.cpp2
-rw-r--r--src/qt/guiutil.cpp6
-rw-r--r--src/qt/guiutil.h3
-rw-r--r--src/qt/openuridialog.cpp2
-rw-r--r--src/qt/optionsdialog.cpp2
-rw-r--r--src/qt/receiverequestdialog.cpp2
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/splashscreen.cpp2
-rw-r--r--src/qt/test/wallettests.cpp1
-rw-r--r--src/qt/transactiondescdialog.cpp3
-rw-r--r--src/qt/utilitydialog.cpp6
-rw-r--r--src/qt/walletcontroller.cpp13
-rw-r--r--src/qt/walletcontroller.h5
-rw-r--r--src/qt/walletmodel.cpp19
-rw-r--r--src/rpc/blockchain.cpp3
-rw-r--r--src/script/script.h2
-rw-r--r--src/support/lockedpool.cpp4
-rw-r--r--src/test/fuzz/integer.cpp6
-rw-r--r--src/test/fuzz/message.cpp48
-rw-r--r--src/test/fuzz/policy_estimator.cpp69
-rw-r--r--src/test/fuzz/rbf.cpp47
-rw-r--r--src/test/fuzz/scriptnum_ops.cpp6
-rw-r--r--src/test/fuzz/util.h18
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/validation_block_tests.cpp34
-rw-r--r--src/util/translation.h14
-rw-r--r--src/validation.cpp19
-rw-r--r--src/validation.h4
-rw-r--r--src/wallet/db.cpp10
-rw-r--r--src/wallet/db.h6
-rw-r--r--src/wallet/feebumper.cpp58
-rw-r--r--src/wallet/feebumper.h21
-rw-r--r--src/wallet/load.cpp20
-rw-r--r--src/wallet/rpcdump.cpp16
-rw-r--r--src/wallet/rpcwallet.cpp147
-rw-r--r--src/wallet/scriptpubkeyman.cpp5
-rw-r--r--src/wallet/scriptpubkeyman.h5
-rw-r--r--src/wallet/test/wallet_tests.cpp124
-rw-r--r--src/wallet/wallet.cpp249
-rw-r--r--src/wallet/wallet.h33
-rw-r--r--src/wallet/walletdb.cpp4
-rw-r--r--src/wallet/walletdb.h4
-rw-r--r--src/wallet/wallettool.cpp5
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py60
-rwxr-xr-xtest/functional/framework_test_script.py44
-rwxr-xr-xtest/functional/p2p_leak.py7
-rwxr-xr-xtest/functional/p2p_segwit.py4
-rwxr-xr-xtest/functional/test_framework/mininode.py30
-rw-r--r--test/functional/test_framework/script.py23
-rwxr-xr-xtest/functional/test_framework/test_framework.py54
-rwxr-xr-xtest/functional/test_framework/test_node.py9
-rw-r--r--test/functional/test_framework/util.py5
-rwxr-xr-xtest/functional/test_runner.py18
-rwxr-xr-xtest/functional/tool_wallet.py14
-rwxr-xr-xtest/functional/wallet_bumpfee.py4
-rwxr-xr-xtest/functional/wallet_multiwallet.py6
-rwxr-xr-xtest/functional/wallet_upgradewallet.py25
80 files changed, 979 insertions, 790 deletions
diff --git a/.travis.yml b/.travis.yml
index 1e49815a4c..63e502cc3e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -128,7 +128,7 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_i686_centos.sh"
- stage: test
- name: 'x86_64 Linux [GOAL: install] [bionic] [previous releases, uses qt5 dev package and some depends packages] [unsigned char]'
+ name: 'x86_64 Linux [GOAL: install] [bionic] [C++17, previous releases, uses qt5 dev package and some depends packages] [unsigned char]'
env: >-
FILE_ENV="./ci/test/00_setup_env_native_qt5.sh"
@@ -149,11 +149,6 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_native_valgrind.sh"
- stage: test
- name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, c++17]'
- env: >-
- FILE_ENV="./ci/test/00_setup_env_native_cxx17.sh"
-
- - stage: test
name: 'x86_64 Linux [GOAL: install] [focal] [no depends, only system libs, sanitizers: fuzzer,address,undefined]'
env: >-
FILE_ENV="./ci/test/00_setup_env_native_fuzz.sh"
diff --git a/ci/test/00_setup_env_native_cxx17.sh b/ci/test/00_setup_env_native_cxx17.sh
deleted file mode 100644
index 9a75276420..0000000000
--- a/ci/test/00_setup_env_native_cxx17.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2020 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export LC_ALL=C.UTF-8
-
-export CONTAINER_NAME=ci_native_cxx17
-export PACKAGES="python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev"
-export NO_DEPENDS=1
-export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-c++17"
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index 87c8b70998..4dedea23bc 100644
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -14,4 +14,4 @@ export RUN_UNIT_TESTS_SEQUENTIAL="true"
export RUN_UNIT_TESTS="false"
export GOAL="install"
export TEST_PREVIOUS_RELEASES=true
-export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
+export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-c++17 --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index d5b38a1d88..153a781b0a 100644
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
@@ -13,7 +13,7 @@ export NO_DEPENDS=1
if [[ "${TRAVIS}" == "true" && "${TRAVIS_REPO_SLUG}" != "bitcoin/bitcoin" ]]; then
export TEST_RUNNER_EXTRA="wallet_disable" # Only run wallet_disable as a smoke test to not hit the 50 min travis time limit
else
- export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
+ export TEST_RUNNER_EXTRA="--exclude rpc_bind --factor=2" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
fi
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++" # TODO enable GUI
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 48db60f086..68ba1bc68e 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -53,6 +53,7 @@ FUZZ_TARGETS = \
test/fuzz/locale \
test/fuzz/merkle_block_deserialize \
test/fuzz/merkleblock \
+ test/fuzz/message \
test/fuzz/messageheader_deserialize \
test/fuzz/multiplication_overflow \
test/fuzz/net_permissions \
@@ -67,6 +68,7 @@ FUZZ_TARGETS = \
test/fuzz/parse_univalue \
test/fuzz/partial_merkle_tree_deserialize \
test/fuzz/partially_signed_transaction_deserialize \
+ test/fuzz/policy_estimator \
test/fuzz/pow \
test/fuzz/prefilled_transaction_deserialize \
test/fuzz/prevector \
@@ -103,6 +105,7 @@ FUZZ_TARGETS = \
test/fuzz/psbt_output_deserialize \
test/fuzz/pub_key_deserialize \
test/fuzz/random \
+ test/fuzz/rbf \
test/fuzz/rolling_bloom_filter \
test/fuzz/script \
test/fuzz/script_deserialize \
@@ -593,6 +596,12 @@ test_fuzz_merkleblock_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_merkleblock_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_merkleblock_SOURCES = test/fuzz/merkleblock.cpp
+test_fuzz_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_message_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_message_SOURCES = test/fuzz/message.cpp
+
test_fuzz_messageheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGEHEADER_DESERIALIZE=1
test_fuzz_messageheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_messageheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -683,6 +692,12 @@ test_fuzz_partially_signed_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMO
test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_partially_signed_transaction_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_policy_estimator_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_policy_estimator_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_policy_estimator_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_policy_estimator_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_policy_estimator_SOURCES = test/fuzz/policy_estimator.cpp
+
test_fuzz_pow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_pow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_pow_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -893,6 +908,12 @@ test_fuzz_random_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_random_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_random_SOURCES = test/fuzz/random.cpp
+test_fuzz_rbf_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_rbf_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_rbf_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_rbf_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_rbf_SOURCES = test/fuzz/rbf.cpp
+
test_fuzz_rolling_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_rolling_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_rolling_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index d658976c3c..86f9a0bf67 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -47,8 +47,6 @@ static void CCoinsCaching(benchmark::State& state)
while (state.KeepRunning()) {
bool success = AreInputsStandard(tx_1, coins);
assert(success);
- CAmount value = coins.GetValueIn(tx_1);
- assert(value == (50 + 21 + 22) * COIN);
}
ECC_Stop();
}
diff --git a/src/coins.cpp b/src/coins.cpp
index 6b4cb2aec7..7b76c13f98 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -233,18 +233,6 @@ unsigned int CCoinsViewCache::GetCacheSize() const {
return cacheCoins.size();
}
-CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
-{
- if (tx.IsCoinBase())
- return 0;
-
- CAmount nResult = 0;
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- nResult += AccessCoin(tx.vin[i].prevout).out.nValue;
-
- return nResult;
-}
-
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
if (!tx.IsCoinBase()) {
diff --git a/src/coins.h b/src/coins.h
index 2aed56c2bd..dcec741998 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -315,16 +315,6 @@ public:
//! Calculate the size of the cache (in bytes)
size_t DynamicMemoryUsage() const;
- /**
- * Amount of bitcoins coming in to a transaction
- * Note that lightweight clients may not know anything besides the hash of previous transactions,
- * so may not be able to calculate this.
- *
- * @param[in] tx transaction for which we are checking input total
- * @return Sum of value of all inputs (scriptSigs)
- */
- CAmount GetValueIn(const CTransaction& tx) const;
-
//! Check whether all prevouts of the transaction are present in the UTXO set represented by this view
bool HaveInputs(const CTransaction& tx) const;
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index a5582e3b2c..0f7848bae1 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -8,6 +8,7 @@
class CWallet;
enum class WalletCreationStatus;
+struct bilingual_str;
namespace interfaces {
class Chain;
@@ -72,12 +73,12 @@ std::vector<std::shared_ptr<CWallet>> GetWallets()
throw std::logic_error("Wallet function called in non-wallet build.");
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::vector<std::string>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
throw std::logic_error("Wallet function called in non-wallet build.");
}
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::shared_ptr<CWallet>& result)
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
{
throw std::logic_error("Wallet function called in non-wallet build.");
}
diff --git a/src/init.cpp b/src/init.cpp
index a213dacbe0..3e2ee4d425 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -708,6 +708,10 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
break; // This error is logged in OpenBlockFile
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
LoadExternalBlockFile(chainparams, file, &pos);
+ if (ShutdownRequested()) {
+ LogPrintf("Shutdown requested. Exit %s\n", __func__);
+ return;
+ }
nFile++;
}
pblocktree->WriteReindexing(false);
@@ -723,6 +727,10 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(chainparams, file);
+ if (ShutdownRequested()) {
+ LogPrintf("Shutdown requested. Exit %s\n", __func__);
+ return;
+ }
} else {
LogPrintf("Warning: Could not open blocks file %s\n", path.string());
}
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 0e7641ae32..c5262e4bc0 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -53,88 +53,6 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<Rec
return true;
}
-class LockImpl : public Chain::Lock, public UniqueLock<RecursiveMutex>
-{
- Optional<int> getHeight() override
- {
- LockAssertion lock(::cs_main);
- int height = ::ChainActive().Height();
- if (height >= 0) {
- return height;
- }
- return nullopt;
- }
- Optional<int> getBlockHeight(const uint256& hash) override
- {
- LockAssertion lock(::cs_main);
- CBlockIndex* block = LookupBlockIndex(hash);
- if (block && ::ChainActive().Contains(block)) {
- return block->nHeight;
- }
- return nullopt;
- }
- uint256 getBlockHash(int height) override
- {
- LockAssertion lock(::cs_main);
- CBlockIndex* block = ::ChainActive()[height];
- assert(block != nullptr);
- return block->GetBlockHash();
- }
- bool haveBlockOnDisk(int height) override
- {
- LockAssertion lock(::cs_main);
- CBlockIndex* block = ::ChainActive()[height];
- return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
- }
- Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
- {
- LockAssertion lock(::cs_main);
- CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height);
- if (block) {
- if (hash) *hash = block->GetBlockHash();
- return block->nHeight;
- }
- return nullopt;
- }
- Optional<int> findFork(const uint256& hash, Optional<int>* height) override
- {
- LockAssertion lock(::cs_main);
- const CBlockIndex* block = LookupBlockIndex(hash);
- const CBlockIndex* fork = block ? ::ChainActive().FindFork(block) : nullptr;
- if (height) {
- if (block) {
- *height = block->nHeight;
- } else {
- height->reset();
- }
- }
- if (fork) {
- return fork->nHeight;
- }
- return nullopt;
- }
- CBlockLocator getTipLocator() override
- {
- LockAssertion lock(::cs_main);
- return ::ChainActive().GetLocator();
- }
- Optional<int> findLocatorFork(const CBlockLocator& locator) override
- {
- LockAssertion lock(::cs_main);
- if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) {
- return fork->nHeight;
- }
- return nullopt;
- }
- bool checkFinalTx(const CTransaction& tx) override
- {
- LockAssertion lock(::cs_main);
- return CheckFinalTx(tx);
- }
-
- using UniqueLock::UniqueLock;
-};
-
class NotificationsProxy : public CValidationInterface
{
public:
@@ -227,12 +145,64 @@ class ChainImpl : public Chain
{
public:
explicit ChainImpl(NodeContext& node) : m_node(node) {}
- std::unique_ptr<Chain::Lock> lock(bool try_lock) override
+ Optional<int> getHeight() override
+ {
+ LOCK(::cs_main);
+ int height = ::ChainActive().Height();
+ if (height >= 0) {
+ return height;
+ }
+ return nullopt;
+ }
+ Optional<int> getBlockHeight(const uint256& hash) override
+ {
+ LOCK(::cs_main);
+ CBlockIndex* block = LookupBlockIndex(hash);
+ if (block && ::ChainActive().Contains(block)) {
+ return block->nHeight;
+ }
+ return nullopt;
+ }
+ uint256 getBlockHash(int height) override
+ {
+ LOCK(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
+ assert(block);
+ return block->GetBlockHash();
+ }
+ bool haveBlockOnDisk(int height) override
+ {
+ LOCK(cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
+ return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
+ }
+ Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
- auto lock = MakeUnique<LockImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
- if (try_lock && lock && !*lock) return {};
- std::unique_ptr<Chain::Lock> result = std::move(lock); // Temporary to avoid CWG 1579
- return result;
+ LOCK(cs_main);
+ CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height);
+ if (block) {
+ if (hash) *hash = block->GetBlockHash();
+ return block->nHeight;
+ }
+ return nullopt;
+ }
+ CBlockLocator getTipLocator() override
+ {
+ LOCK(cs_main);
+ return ::ChainActive().GetLocator();
+ }
+ bool checkFinalTx(const CTransaction& tx) override
+ {
+ LOCK(cs_main);
+ return CheckFinalTx(tx);
+ }
+ Optional<int> findLocatorFork(const CBlockLocator& locator) override
+ {
+ LOCK(cs_main);
+ if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) {
+ return fork->nHeight;
+ }
+ return nullopt;
}
bool findBlock(const uint256& hash, const FoundBlock& block) override
{
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index fb77c81cdc..e33fe54ac8 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -59,12 +59,7 @@ public:
//! internal workings of the bitcoin node, and not being very convenient to use.
//! Chain methods should be cleaned up and simplified over time. Examples:
//!
-//! * The Chain::lock() method, which lets clients delay chain tip updates
-//! should be removed when clients are able to respond to updates
-//! asynchronously
-//! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269).
-//!
-//! * The initMessage() and showProgress() methods which the wallet uses to send
+//! * The initMessages() and showProgress() methods which the wallet uses to send
//! notifications to the GUI should go away when GUI and wallet can directly
//! communicate with each other without going through the node
//! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096).
@@ -72,68 +67,53 @@ public:
//! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC
//! methods can go away if wallets listen for HTTP requests on their own
//! ports instead of registering to handle requests on the node HTTP port.
+//!
+//! * Move fee estimation queries to an asynchronous interface and let the
+//! wallet cache it, fee estimation being driven by node mempool, wallet
+//! should be the consumer.
+//!
+//! * The `guessVerificationProgress`, `getBlockHeight`, `getBlockHash`, etc
+//! methods can go away if rescan logic is moved on the node side, and wallet
+//! only register rescan request.
class Chain
{
public:
virtual ~Chain() {}
- //! Interface for querying locked chain state, used by legacy code that
- //! assumes state won't change between calls. New code should avoid using
- //! the Lock interface and instead call higher-level Chain methods
- //! that return more information so the chain doesn't need to stay locked
- //! between calls.
- class Lock
- {
- public:
- virtual ~Lock() {}
-
- //! Get current chain height, not including genesis block (returns 0 if
- //! chain only contains genesis block, nullopt if chain does not contain
- //! any blocks).
- virtual Optional<int> getHeight() = 0;
-
- //! Get block height above genesis block. Returns 0 for genesis block,
- //! 1 for following block, and so on. Returns nullopt for a block not
- //! included in the current chain.
- virtual Optional<int> getBlockHeight(const uint256& hash) = 0;
-
- //! Get block hash. Height must be valid or this function will abort.
- virtual uint256 getBlockHash(int height) = 0;
-
- //! Check that the block is available on disk (i.e. has not been
- //! pruned), and contains transactions.
- virtual bool haveBlockOnDisk(int height) = 0;
-
- //! Return height of the first block in the chain with timestamp equal
- //! or greater than the given time and height equal or greater than the
- //! given height, or nullopt if there is no block with a high enough
- //! timestamp and height. Also return the block hash as an optional output parameter
- //! (to avoid the cost of a second lookup in case this information is needed.)
- virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
-
- //! Return height of the specified block if it is on the chain, otherwise
- //! return the height of the highest block on chain that's an ancestor
- //! of the specified block, or nullopt if there is no common ancestor.
- //! Also return the height of the specified block as an optional output
- //! parameter (to avoid the cost of a second hash lookup in case this
- //! information is desired).
- virtual Optional<int> findFork(const uint256& hash, Optional<int>* height) = 0;
-
- //! Get locator for the current chain tip.
- virtual CBlockLocator getTipLocator() = 0;
-
- //! Return height of the highest block on chain in common with the locator,
- //! which will either be the original block used to create the locator,
- //! or one of its ancestors.
- virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
-
- //! Check if transaction will be final given chain height current time.
- virtual bool checkFinalTx(const CTransaction& tx) = 0;
- };
+ //! Get current chain height, not including genesis block (returns 0 if
+ //! chain only contains genesis block, nullopt if chain does not contain
+ //! any blocks)
+ virtual Optional<int> getHeight() = 0;
+
+ //! Get block height above genesis block. Returns 0 for genesis block,
+ //! 1 for following block, and so on. Returns nullopt for a block not
+ //! included in the current chain.
+ virtual Optional<int> getBlockHeight(const uint256& hash) = 0;
+
+ //! Get block hash. Height must be valid or this function will abort.
+ virtual uint256 getBlockHash(int height) = 0;
+
+ //! Check that the block is available on disk (i.e. has not been
+ //! pruned), and contains transactions.
+ virtual bool haveBlockOnDisk(int height) = 0;
+
+ //! Return height of the first block in the chain with timestamp equal
+ //! or greater than the given time and height equal or greater than the
+ //! given height, or nullopt if there is no block with a high enough
+ //! timestamp and height. Also return the block hash as an optional output parameter
+ //! (to avoid the cost of a second lookup in case this information is needed.)
+ virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
+
+ //! Get locator for the current chain tip.
+ virtual CBlockLocator getTipLocator() = 0;
+
+ //! Return height of the highest block on chain in common with the locator,
+ //! which will either be the original block used to create the locator,
+ //! or one of its ancestors.
+ virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
- //! Return Lock interface. Chain is locked when this is called, and
- //! unlocked when the returned interface is freed.
- virtual std::unique_ptr<Lock> lock(bool try_lock = false) = 0;
+ //! Check if transaction will be final given chain height current time.
+ virtual bool checkFinalTx(const CTransaction& tx) = 0;
//! Return whether node has the block and optionally return block metadata
//! or contents.
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 5ebbd61584..8564819e6a 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -43,8 +43,8 @@ class CWallet;
fs::path GetWalletDir();
std::vector<fs::path> ListWalletDir();
std::vector<std::shared_ptr<CWallet>> GetWallets();
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::vector<std::string>& warnings);
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::shared_ptr<CWallet>& result);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings);
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet);
namespace interfaces {
@@ -259,11 +259,11 @@ public:
}
return wallets;
}
- std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::vector<std::string>& warnings) override
+ std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{
return MakeWallet(LoadWallet(*m_context.chain, name, error, warnings));
}
- std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, WalletCreationStatus& status) override
+ std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) override
{
std::shared_ptr<CWallet> wallet;
status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 53a20886cd..db9b42b293 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -27,9 +27,10 @@ class Coin;
class RPCTimerInterface;
class UniValue;
class proxyType;
+enum class WalletCreationStatus;
struct CNodeStateStats;
struct NodeContext;
-enum class WalletCreationStatus;
+struct bilingual_str;
namespace interfaces {
class Handler;
@@ -201,10 +202,10 @@ public:
//! Attempts to load a wallet from file or directory.
//! The loaded wallet is also notified to handlers previously registered
//! with handleLoadWallet.
- virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::vector<std::string>& warnings) = 0;
+ virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
//! Create a wallet from file
- virtual std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, WalletCreationStatus& status) = 0;
+ virtual std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) = 0;
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string& message)>;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 152917ac60..13b034936b 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -60,7 +60,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
}
//! Construct wallet tx status struct.
-WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
+WalletTxStatus MakeWalletTxStatus(CWallet& wallet, const CWalletTx& wtx)
{
WalletTxStatus result;
result.block_height = wtx.m_confirm.block_height > 0 ? wtx.m_confirm.block_height : std::numeric_limits<int>::max();
@@ -68,8 +68,8 @@ WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const C
result.depth_in_main_chain = wtx.GetDepthInMainChain();
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
- result.is_final = locked_chain.checkFinalTx(*wtx.tx);
- result.is_trusted = wtx.IsTrusted(locked_chain);
+ result.is_final = wallet.chain().checkFinalTx(*wtx.tx);
+ result.is_trusted = wtx.IsTrusted();
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
result.is_in_main_chain = wtx.IsInMainChain();
@@ -196,25 +196,21 @@ public:
}
void lockCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->LockCoin(output);
}
void unlockCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->UnlockCoin(output);
}
bool isLockedCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->IsLockedCoin(output.hash, output.n);
}
void listLockedCoins(std::vector<COutPoint>& outputs) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->ListLockedCoins(outputs);
}
@@ -223,12 +219,11 @@ public:
bool sign,
int& change_pos,
CAmount& fee,
- std::string& fail_reason) override
+ bilingual_str& fail_reason) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
CTransactionRef tx;
- if (!m_wallet->CreateTransaction(*locked_chain, recipients, tx, fee, change_pos,
+ if (!m_wallet->CreateTransaction(recipients, tx, fee, change_pos,
fail_reason, coin_control, sign)) {
return {};
}
@@ -238,14 +233,12 @@ public:
WalletValueMap value_map,
WalletOrderForm order_form) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
m_wallet->CommitTransaction(std::move(tx), std::move(value_map), std::move(order_form));
}
bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet->TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->AbandonTransaction(txid);
}
@@ -255,7 +248,7 @@ public:
}
bool createBumpTransaction(const uint256& txid,
const CCoinControl& coin_control,
- std::vector<std::string>& errors,
+ std::vector<bilingual_str>& errors,
CAmount& old_fee,
CAmount& new_fee,
CMutableTransaction& mtx) override
@@ -265,7 +258,7 @@ public:
bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(*m_wallet.get(), mtx); }
bool commitBumpTransaction(const uint256& txid,
CMutableTransaction&& mtx,
- std::vector<std::string>& errors,
+ std::vector<bilingual_str>& errors,
uint256& bumped_txid) override
{
return feebumper::CommitTransaction(*m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) ==
@@ -273,7 +266,6 @@ public:
}
CTransactionRef getTx(const uint256& txid) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
auto mi = m_wallet->mapWallet.find(txid);
if (mi != m_wallet->mapWallet.end()) {
@@ -283,7 +275,6 @@ public:
}
WalletTx getWalletTx(const uint256& txid) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
auto mi = m_wallet->mapWallet.find(txid);
if (mi != m_wallet->mapWallet.end()) {
@@ -293,7 +284,6 @@ public:
}
std::vector<WalletTx> getWalletTxs() override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
std::vector<WalletTx> result;
result.reserve(m_wallet->mapWallet.size());
@@ -307,10 +297,6 @@ public:
int& num_blocks,
int64_t& block_time) override
{
- auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
- if (!locked_chain) {
- return false;
- }
TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
@@ -322,7 +308,7 @@ public:
num_blocks = m_wallet->GetLastBlockHeight();
block_time = -1;
CHECK_NONFATAL(m_wallet->chain().findBlock(m_wallet->GetLastBlockHash(), FoundBlock().time(block_time)));
- tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
+ tx_status = MakeWalletTxStatus(*m_wallet, mi->second);
return true;
}
WalletTx getWalletTxDetails(const uint256& txid,
@@ -331,14 +317,13 @@ public:
bool& in_mempool,
int& num_blocks) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
auto mi = m_wallet->mapWallet.find(txid);
if (mi != m_wallet->mapWallet.end()) {
- num_blocks = locked_chain->getHeight().get_value_or(-1);
+ num_blocks = m_wallet->GetLastBlockHeight();
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
- tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
+ tx_status = MakeWalletTxStatus(*m_wallet, mi->second);
return MakeWalletTx(*m_wallet, mi->second);
}
return {};
@@ -368,8 +353,6 @@ public:
}
bool tryGetBalances(WalletBalances& balances, int& num_blocks, bool force, int cached_num_blocks) override
{
- auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
- if (!locked_chain) return false;
TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
@@ -386,34 +369,29 @@ public:
}
isminetype txinIsMine(const CTxIn& txin) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->IsMine(txin);
}
isminetype txoutIsMine(const CTxOut& txout) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->IsMine(txout);
}
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->GetDebit(txin, filter);
}
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
return m_wallet->GetCredit(txout, filter);
}
CoinsList listCoins() override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
CoinsList result;
- for (const auto& entry : m_wallet->ListCoins(*locked_chain)) {
+ for (const auto& entry : m_wallet->ListCoins()) {
auto& group = result[entry.first];
for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
@@ -424,7 +402,6 @@ public:
}
std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
{
- auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
std::vector<WalletTxOut> result;
result.reserve(outputs.size());
@@ -496,6 +473,7 @@ public:
{
return MakeHandler(m_wallet->NotifyCanGetAddressesChanged.connect(fn));
}
+ CWallet* wallet() override { return m_wallet.get(); }
std::shared_ptr<CWallet> m_wallet;
};
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 5d870c5e3d..f35335c69f 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -31,6 +31,7 @@ enum class TransactionError;
enum isminetype : unsigned int;
struct CRecipient;
struct PartiallySignedTransaction;
+struct bilingual_str;
typedef uint8_t isminefilter;
namespace interfaces {
@@ -136,7 +137,7 @@ public:
bool sign,
int& change_pos,
CAmount& fee,
- std::string& fail_reason) = 0;
+ bilingual_str& fail_reason) = 0;
//! Commit transaction.
virtual void commitTransaction(CTransactionRef tx,
@@ -155,7 +156,7 @@ public:
//! Create bump transaction.
virtual bool createBumpTransaction(const uint256& txid,
const CCoinControl& coin_control,
- std::vector<std::string>& errors,
+ std::vector<bilingual_str>& errors,
CAmount& old_fee,
CAmount& new_fee,
CMutableTransaction& mtx) = 0;
@@ -166,7 +167,7 @@ public:
//! Commit bump transaction.
virtual bool commitBumpTransaction(const uint256& txid,
CMutableTransaction&& mtx,
- std::vector<std::string>& errors,
+ std::vector<bilingual_str>& errors,
uint256& bumped_txid) = 0;
//! Get a transaction.
@@ -300,6 +301,9 @@ public:
//! Register handler for keypool changed messages.
using CanGetAddressesChangedFn = std::function<void()>;
virtual std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) = 0;
+
+ //! Return pointer to internal wallet class, useful for testing.
+ virtual CWallet* wallet() { return nullptr; }
};
//! Information about one wallet address.
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 00ccbc32f9..58b3e8aedc 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -324,8 +324,6 @@ public:
// Return sum of txouts.
CAmount GetValueOut() const;
- // GetValueIn() is a method on CCoinsViewCache, because
- // inputs must be known to compute value in.
/**
* Get the total transaction size in bytes, including witness data.
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 1aaf33c6a4..f48f28d03a 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -136,6 +136,8 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
AddressBookPage::~AddressBookPage()
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index 67e7704551..3d1963b6e6 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -10,6 +10,7 @@
#include <qt/forms/ui_askpassphrasedialog.h>
#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <support/allocators/secure.h>
@@ -75,6 +76,8 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri
connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
AskPassphraseDialog::~AskPassphraseDialog()
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 71e4d59ea7..cf2de1a417 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -211,6 +211,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
#ifdef Q_OS_MAC
m_app_nap_inhibitor = new CAppNapInhibitor;
#endif
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
BitcoinGUI::~BitcoinGUI()
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 1889f5e056..b46f416b6c 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -134,6 +134,8 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
ui->radioTreeMode->click();
if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt())));
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
CoinControlDialog::~CoinControlDialog()
diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp
index 4711412ac4..e0af9a20a8 100644
--- a/src/qt/editaddressdialog.cpp
+++ b/src/qt/editaddressdialog.cpp
@@ -43,6 +43,8 @@ EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) :
GUIUtil::ItemDelegate* delegate = new GUIUtil::ItemDelegate(mapper);
connect(delegate, &GUIUtil::ItemDelegate::keyEscapePressed, this, &EditAddressDialog::reject);
mapper->setItemDelegate(delegate);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
EditAddressDialog::~EditAddressDialog()
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index a54b65961c..af86fe5d27 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -54,6 +54,7 @@
#include <QSettings>
#include <QSize>
#include <QString>
+#include <QShortcut>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
#include <QUrlQuery>
@@ -378,6 +379,11 @@ void bringToFront(QWidget* w)
}
}
+void handleCloseWindowShortcut(QWidget* w)
+{
+ QObject::connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), w), &QShortcut::activated, w, &QWidget::close);
+}
+
void openDebugLogfile()
{
fs::path pathDebug = GetDataDir() / "debug.log";
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index e571262443..0d9293fbad 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -124,6 +124,9 @@ namespace GUIUtil
// Activate, show and raise the widget
void bringToFront(QWidget* w);
+ // Set shortcut to close window
+ void handleCloseWindowShortcut(QWidget* w);
+
// Open debug.log
void openDebugLogfile();
diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp
index b9dea2f8bf..9a3d43c2a6 100644
--- a/src/qt/openuridialog.cpp
+++ b/src/qt/openuridialog.cpp
@@ -15,6 +15,8 @@ OpenURIDialog::OpenURIDialog(QWidget *parent) :
ui(new Ui::OpenURIDialog)
{
ui->setupUi(this);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
OpenURIDialog::~OpenURIDialog()
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 8ee6c947e6..ae6aeb7709 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -135,6 +135,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui->minimizeToTray->setChecked(false);
ui->minimizeToTray->setEnabled(false);
}
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
OptionsDialog::~OptionsDialog()
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index b4fae7d78d..30bd5c6a5a 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -30,6 +30,8 @@ ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
#endif
connect(ui->btnSaveAs, &QPushButton::clicked, ui->lblQRCode, &QRImageWidget::saveImage);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
ReceiveRequestDialog::~ReceiveRequestDialog()
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 18a7e9b021..bfe316bdcd 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -498,6 +498,8 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt();
clear();
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
RPCConsole::~RPCConsole()
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 67c2c71e9c..5cfbb67449 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -47,6 +47,8 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformS
ui->signatureOut_SM->setFont(GUIUtil::fixedPitchFont());
ui->signatureIn_VM->setFont(GUIUtil::fixedPitchFont());
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
SignVerifyMessageDialog::~SignVerifyMessageDialog()
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index d8fe4a9238..ced6a299d5 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -127,6 +127,8 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
subscribeToCoreSignals();
installEventFilter(this);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
SplashScreen::~SplashScreen()
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 94a1e0a63d..2ee9ae0d86 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -145,7 +145,6 @@ void TestGUI(interfaces::Node& node)
wallet->LoadWallet(firstRun);
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
- auto locked_chain = wallet->chain().lock();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp
index ca72720e00..715e312b19 100644
--- a/src/qt/transactiondescdialog.cpp
+++ b/src/qt/transactiondescdialog.cpp
@@ -5,6 +5,7 @@
#include <qt/transactiondescdialog.h>
#include <qt/forms/ui_transactiondescdialog.h>
+#include <qt/guiutil.h>
#include <qt/transactiontablemodel.h>
#include <QModelIndex>
@@ -17,6 +18,8 @@ TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *pa
setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxHashRole).toString()));
QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString();
ui->detailText->setHtml(desc);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
TransactionDescDialog::~TransactionDescDialog()
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index d16feba1bb..01922cf996 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -10,6 +10,8 @@
#include <qt/forms/ui_helpmessagedialog.h>
+#include <qt/guiutil.h>
+
#include <clientversion.h>
#include <init.h>
#include <util/system.h>
@@ -102,6 +104,8 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
ui->scrollArea->setVisible(false);
ui->aboutLogo->setVisible(false);
}
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
HelpMessageDialog::~HelpMessageDialog()
@@ -141,6 +145,8 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f):
tr("%1 is shutting down...").arg(PACKAGE_NAME) + "<br /><br />" +
tr("Do not shut down the computer until this window disappears.")));
setLayout(layout);
+
+ GUIUtil::handleCloseWindowShortcut(this);
}
QWidget* ShutdownWindow::showShutdownWindow(QMainWindow* window)
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 9c1488fb0e..7cde3ca30b 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -14,6 +14,7 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
+#include <util/translation.h>
#include <wallet/wallet.h>
#include <algorithm>
@@ -244,10 +245,10 @@ void CreateWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.empty()) {
- QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message));
+ if (!m_error_message.original.empty()) {
+ QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
- QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, "\n")));
+ QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, "\n", OpTranslated)));
}
if (m_wallet_model) Q_EMIT created(m_wallet_model);
@@ -285,10 +286,10 @@ void OpenWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.empty()) {
- QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message));
+ if (!m_error_message.original.empty()) {
+ QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
- QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, "\n")));
+ QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, "\n", OpTranslated)));
}
if (m_wallet_model) Q_EMIT opened(m_wallet_model);
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 3808b7d8bf..24dd83adf7 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -8,6 +8,7 @@
#include <qt/sendcoinsrecipient.h>
#include <support/allocators/secure.h>
#include <sync.h>
+#include <util/translation.h>
#include <map>
#include <memory>
@@ -104,8 +105,8 @@ protected:
QWidget* const m_parent_widget;
QProgressDialog* m_progress_dialog{nullptr};
WalletModel* m_wallet_model{nullptr};
- std::string m_error_message;
- std::vector<std::string> m_warning_message;
+ bilingual_str m_error_message;
+ std::vector<bilingual_str> m_warning_message;
};
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e7581cd64e..70ee7f4917 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -24,6 +24,7 @@
#include <psbt.h>
#include <ui_interface.h>
#include <util/system.h> // for GetBoolArg
+#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/wallet.h> // for CRecipient
@@ -185,10 +186,10 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
{
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
- std::string strFailReason;
+ bilingual_str error;
auto& newTx = transaction.getWtx();
- newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason);
+ newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, error);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
@@ -199,8 +200,8 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
{
return SendCoinsReturn(AmountWithFeeExceedsBalance);
}
- Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason),
- CClientUIInterface::MSG_ERROR);
+ Q_EMIT message(tr("Send Coins"), QString::fromStdString(error.translated),
+ CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed;
}
@@ -482,14 +483,14 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
{
CCoinControl coin_control;
coin_control.m_signal_bip125_rbf = true;
- std::vector<std::string> errors;
+ std::vector<bilingual_str> errors;
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
if (!m_wallet->createBumpTransaction(hash, coin_control, errors, old_fee, new_fee, mtx)) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
- (errors.size() ? QString::fromStdString(errors[0]) : "") +")");
- return false;
+ (errors.size() ? QString::fromStdString(errors[0].translated) : "") +")");
+ return false;
}
const bool create_psbt = m_wallet->privateKeysDisabled();
@@ -551,8 +552,8 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
// commit the bumped transaction
if(!m_wallet->commitBumpTransaction(hash, std::move(mtx), errors, new_hash)) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
- QString::fromStdString(errors[0])+")");
- return false;
+ QString::fromStdString(errors[0].translated)+")");
+ return false;
}
return true;
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 827b83d67e..f7ccbae706 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -2242,8 +2242,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
{
RPCHelpMan{
"dumptxoutset",
- "\nWrite the serialized UTXO set to disk.\n"
- "Incidentally flushes the latest coinsdb (leveldb) to disk.\n",
+ "\nWrite the serialized UTXO set to disk.\n",
{
{"path",
RPCArg::Type::STR,
diff --git a/src/script/script.h b/src/script/script.h
index daf4224530..773ffbb985 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -329,7 +329,7 @@ public:
std::vector<unsigned char> result;
const bool neg = value < 0;
- uint64_t absvalue = neg ? -value : value;
+ uint64_t absvalue = neg ? ~static_cast<uint64_t>(value) + 1 : static_cast<uint64_t>(value);
while(absvalue)
{
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 9b14637b95..f17b539e09 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -253,8 +253,10 @@ void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
}
if (addr) {
*lockingSuccess = mlock(addr, len) == 0;
-#ifdef MADV_DONTDUMP
+#if defined(MADV_DONTDUMP) // Linux
madvise(addr, len, MADV_DONTDUMP);
+#elif defined(MADV_NOCORE) // FreeBSD
+ madvise(addr, len, MADV_NOCORE);
#endif
}
return addr;
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index d5b5ec3c9a..35d6804d4f 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -148,11 +148,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const CScriptNum script_num{i64};
(void)script_num.getint();
- // Avoid negation failure:
- // script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself
- if (script_num != CScriptNum{std::numeric_limits<int64_t>::min()}) {
- (void)script_num.getvch();
- }
+ (void)script_num.getvch();
const arith_uint256 au256 = UintToArith256(u256);
assert(ArithToUint256(au256) == u256);
diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp
new file mode 100644
index 0000000000..dfa98a812b
--- /dev/null
+++ b/src/test/fuzz/message.cpp
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <key_io.h>
+#include <optional.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <util/message.h>
+#include <util/strencodings.h>
+
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::string random_message = fuzzed_data_provider.ConsumeRandomLengthString(1024);
+ {
+ const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ CKey private_key;
+ private_key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
+ std::string signature;
+ const bool message_signed = MessageSign(private_key, random_message, signature);
+ if (private_key.IsValid()) {
+ assert(message_signed);
+ const MessageVerificationResult verification_result = MessageVerify(EncodeDestination(PKHash(private_key.GetPubKey().GetID())), signature, random_message);
+ assert(verification_result == MessageVerificationResult::OK);
+ }
+ }
+ {
+ (void)MessageHash(random_message);
+ (void)MessageVerify(fuzzed_data_provider.ConsumeRandomLengthString(1024), fuzzed_data_provider.ConsumeRandomLengthString(1024), random_message);
+ (void)SigningResultString(fuzzed_data_provider.PickValueInArray({SigningResult::OK, SigningResult::PRIVATE_KEY_NOT_AVAILABLE, SigningResult::SIGNING_FAILED}));
+ }
+}
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
new file mode 100644
index 0000000000..201f49c87b
--- /dev/null
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <optional.h>
+#include <policy/fees.h>
+#include <primitives/transaction.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <txmempool.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ CBlockPolicyEstimator block_policy_estimator;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) {
+ case 0: {
+ const Optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!mtx) {
+ break;
+ }
+ const CTransaction tx{*mtx};
+ block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool());
+ if (fuzzed_data_provider.ConsumeBool()) {
+ (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool());
+ }
+ break;
+ }
+ case 1: {
+ std::vector<CTxMemPoolEntry> mempool_entries;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const Optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!mtx) {
+ break;
+ }
+ const CTransaction tx{*mtx};
+ mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
+ }
+ std::vector<const CTxMemPoolEntry*> ptrs;
+ ptrs.reserve(mempool_entries.size());
+ for (const CTxMemPoolEntry& mempool_entry : mempool_entries) {
+ ptrs.push_back(&mempool_entry);
+ }
+ block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral<unsigned int>(), ptrs);
+ break;
+ }
+ case 2: {
+ (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool());
+ break;
+ }
+ case 3: {
+ block_policy_estimator.FlushUnconfirmed();
+ break;
+ }
+ }
+ (void)block_policy_estimator.estimateFee(fuzzed_data_provider.ConsumeIntegral<int>());
+ EstimationResult result;
+ (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}), fuzzed_data_provider.ConsumeBool() ? &result : nullptr);
+ FeeCalculation fee_calculation;
+ (void)block_policy_estimator.estimateSmartFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr, fuzzed_data_provider.ConsumeBool());
+ (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}));
+ }
+}
diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp
new file mode 100644
index 0000000000..eb54b05df9
--- /dev/null
+++ b/src/test/fuzz/rbf.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <optional.h>
+#include <policy/rbf.h>
+#include <primitives/transaction.h>
+#include <sync.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <txmempool.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ Optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!mtx) {
+ return;
+ }
+ CTxMemPool pool;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const Optional<CMutableTransaction> another_mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!another_mtx) {
+ break;
+ }
+ const CTransaction another_tx{*another_mtx};
+ if (fuzzed_data_provider.ConsumeBool() && !mtx->vin.empty()) {
+ mtx->vin[0].prevout = COutPoint{another_tx.GetHash(), 0};
+ }
+ LOCK2(cs_main, pool.cs);
+ pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, another_tx));
+ }
+ const CTransaction tx{*mtx};
+ if (fuzzed_data_provider.ConsumeBool()) {
+ LOCK2(cs_main, pool.cs);
+ pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
+ }
+ {
+ LOCK(pool.cs);
+ (void)IsRBFOptIn(tx, pool);
+ }
+}
diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp
index 42b1432f13..f4e079fb89 100644
--- a/src/test/fuzz/scriptnum_ops.cpp
+++ b/src/test/fuzz/scriptnum_ops.cpp
@@ -129,10 +129,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
(void)script_num.getint();
- // Avoid negation failure:
- // script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself
- if (script_num != CScriptNum{std::numeric_limits<int64_t>::min()}) {
- (void)script_num.getvch();
- }
+ (void)script_num.getvch();
}
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 9c7b0d47a2..501bb1de5a 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -8,12 +8,15 @@
#include <amount.h>
#include <arith_uint256.h>
#include <attributes.h>
+#include <consensus/consensus.h>
#include <optional.h>
+#include <primitives/transaction.h>
#include <script/script.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <txmempool.h>
#include <uint256.h>
#include <version.h>
@@ -97,6 +100,21 @@ NODISCARD inline arith_uint256 ConsumeArithUInt256(FuzzedDataProvider& fuzzed_da
return UintToArith256(ConsumeUInt256(fuzzed_data_provider));
}
+NODISCARD inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
+{
+ // Avoid:
+ // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
+ //
+ // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
+ const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
+ assert(MoneyRange(fee));
+ const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
+ const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
+ return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
+}
+
template <typename T>
NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept
{
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 4d50845256..9a490aaf6b 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -14,7 +14,7 @@ BOOST_AUTO_TEST_CASE(basic_sanity)
{
BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test");
BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
- BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "openssl ECC test");
+ BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 9d0ae56c3f..ddbc68f8e2 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -305,7 +305,6 @@ BOOST_AUTO_TEST_CASE(test_Get)
t1.vout[0].scriptPubKey << OP_1;
BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins));
- BOOST_CHECK_EQUAL(coins.GetValueIn(CTransaction(t1)), (50+21+22)*CENT);
}
static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index b8af869b8e..c345f1eafb 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -340,4 +340,38 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
rpc_thread.join();
}
}
+
+BOOST_AUTO_TEST_CASE(witness_commitment_index)
+{
+ CScript pubKey;
+ pubKey << 1 << OP_TRUE;
+ auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey);
+ CBlock pblock = ptemplate->block;
+
+ CTxOut witness;
+ witness.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
+ witness.scriptPubKey[0] = OP_RETURN;
+ witness.scriptPubKey[1] = 0x24;
+ witness.scriptPubKey[2] = 0xaa;
+ witness.scriptPubKey[3] = 0x21;
+ witness.scriptPubKey[4] = 0xa9;
+ witness.scriptPubKey[5] = 0xed;
+
+ // A witness larger than the minimum size is still valid
+ CTxOut min_plus_one = witness;
+ min_plus_one.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT + 1);
+
+ CTxOut invalid = witness;
+ invalid.scriptPubKey[0] = OP_VERIFY;
+
+ CMutableTransaction txCoinbase(*pblock.vtx[0]);
+ txCoinbase.vout.resize(4);
+ txCoinbase.vout[0] = witness;
+ txCoinbase.vout[1] = witness;
+ txCoinbase.vout[2] = min_plus_one;
+ txCoinbase.vout[3] = invalid;
+ pblock.vtx[0] = MakeTransactionRef(std::move(txCoinbase));
+
+ BOOST_CHECK_EQUAL(GetWitnessCommitmentIndex(pblock), 2);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util/translation.h b/src/util/translation.h
index fc45da440a..b2d964c977 100644
--- a/src/util/translation.h
+++ b/src/util/translation.h
@@ -18,6 +18,20 @@ struct bilingual_str {
std::string translated;
};
+inline bilingual_str operator+(const bilingual_str& lhs, const bilingual_str& rhs)
+{
+ return bilingual_str{
+ lhs.original + rhs.original,
+ lhs.translated + rhs.translated};
+}
+
+/** Mark a bilingual_str as untranslated */
+inline static bilingual_str Untranslated(std::string original) { return {original, original}; }
+/** Unary operator to return the original */
+inline static std::string OpOriginal(const bilingual_str& b) { return b.original; }
+/** Unary operator to return the translation */
+inline static std::string OpTranslated(const bilingual_str& b) { return b.translated; }
+
namespace tinyformat {
template <typename... Args>
bilingual_str format(const bilingual_str& fmt, const Args&... args)
diff --git a/src/validation.cpp b/src/validation.cpp
index 8632636637..a924af90af 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3386,7 +3386,14 @@ int GetWitnessCommitmentIndex(const CBlock& block)
int commitpos = -1;
if (!block.vtx.empty()) {
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
- if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) {
+ const CTxOut& vout = block.vtx[0]->vout[o];
+ if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
+ vout.scriptPubKey[0] == OP_RETURN &&
+ vout.scriptPubKey[1] == 0x24 &&
+ vout.scriptPubKey[2] == 0xaa &&
+ vout.scriptPubKey[3] == 0x21 &&
+ vout.scriptPubKey[4] == 0xa9 &&
+ vout.scriptPubKey[5] == 0xed) {
commitpos = o;
}
}
@@ -3417,7 +3424,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
CTxOut out;
out.nValue = 0;
- out.scriptPubKey.resize(38);
+ out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
out.scriptPubKey[0] = OP_RETURN;
out.scriptPubKey[1] = 0x24;
out.scriptPubKey[2] = 0xaa;
@@ -4637,7 +4644,7 @@ bool LoadGenesisBlock(const CChainParams& chainparams)
return ::ChainstateActive().LoadGenesisBlock(chainparams);
}
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp)
+void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
@@ -4649,7 +4656,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE+8, SER_DISK, CLIENT_VERSION);
uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) {
- boost::this_thread::interruption_point();
+ if (ShutdownRequested()) return;
blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure
@@ -4754,9 +4761,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
} catch (const std::runtime_error& e) {
AbortNode(std::string("System error: ") + e.what());
}
- if (nLoaded > 0)
- LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
- return nLoaded > 0;
+ LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
}
void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
diff --git a/src/validation.h b/src/validation.h
index 91b1ba6497..c4a5cc4593 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -92,6 +92,8 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB
// Setting the target to >= 550 MiB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
+/** Minimum size of a witness commitment structure. Defined in BIP 141. **/
+static constexpr size_t MINIMUM_WITNESS_COMMITMENT{38};
struct BlockHasher
{
@@ -180,7 +182,7 @@ FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
fs::path GetBlockPosFilename(const FlatFilePos &pos);
/** Import blocks from an external file */
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp = nullptr);
+void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr);
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
/** Load the block tree and coins database from disk,
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 67be4d85d2..3f7c2d09cc 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -393,7 +393,7 @@ bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, boo
return fSuccess;
}
-bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& errorStr)
+bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
{
std::string walletFile;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
@@ -403,14 +403,14 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er
LogPrintf("Using wallet %s\n", file_path.string());
if (!env->Open(true /* retry */)) {
- errorStr = strprintf(_("Error initializing wallet database environment %s!").translated, walletDir);
+ errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
return false;
}
return true;
}
-bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<std::string>& warnings, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
+bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
{
std::string walletFile;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
@@ -425,12 +425,12 @@ bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<st
warnings.push_back(strprintf(_("Warning: Wallet file corrupt, data salvaged!"
" Original %s saved as %s in %s; if"
" your balance or transactions are incorrect you should"
- " restore from a backup.").translated,
+ " restore from a backup."),
walletFile, backup_filename, walletDir));
}
if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL)
{
- errorStr = strprintf(_("%s corrupt, salvage failed").translated, walletFile);
+ errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
return false;
}
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 1c5f42abca..1bf3375475 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -21,6 +21,8 @@
#include <db_cxx.h>
+struct bilingual_str;
+
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;
@@ -242,9 +244,9 @@ public:
ideal to be called periodically */
static bool PeriodicFlush(BerkeleyDatabase& database);
/* verifies the database environment */
- static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr);
+ static bool VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& file_path, std::vector<std::string>& warnings, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
+ static bool VerifyDatabaseFile(const fs::path& file_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
template <typename K, typename T>
bool Read(const K& key, T& value)
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index dafa022ded..cacf306891 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -3,44 +3,45 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <interfaces/chain.h>
-#include <wallet/coincontrol.h>
-#include <wallet/feebumper.h>
-#include <wallet/fees.h>
-#include <wallet/wallet.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/system.h>
+#include <util/translation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/feebumper.h>
+#include <wallet/fees.h>
+#include <wallet/wallet.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
-static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
if (wallet.HasWalletSpend(wtx.GetHash())) {
- errors.push_back("Transaction has descendants in the wallet");
+ errors.push_back(Untranslated("Transaction has descendants in the wallet"));
return feebumper::Result::INVALID_PARAMETER;
}
{
if (wallet.chain().hasDescendantsInMempool(wtx.GetHash())) {
- errors.push_back("Transaction has descendants in the mempool");
+ errors.push_back(Untranslated("Transaction has descendants in the mempool"));
return feebumper::Result::INVALID_PARAMETER;
}
}
if (wtx.GetDepthInMainChain() != 0) {
- errors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
+ errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));
return feebumper::Result::WALLET_ERROR;
}
if (!SignalsOptInRBF(*wtx.tx)) {
- errors.push_back("Transaction is not BIP 125 replaceable");
+ errors.push_back(Untranslated("Transaction is not BIP 125 replaceable"));
return feebumper::Result::WALLET_ERROR;
}
if (wtx.mapValue.count("replaced_by_txid")) {
- errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid")));
+ errors.push_back(strprintf(Untranslated("Cannot bump transaction %s which was already bumped by transaction %s"), wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid")));
return feebumper::Result::WALLET_ERROR;
}
@@ -48,7 +49,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
if (!wallet.IsAllFromMe(*wtx.tx, filter)) {
- errors.push_back("Transaction contains inputs that don't belong to this wallet");
+ errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
return feebumper::Result::WALLET_ERROR;
}
@@ -57,7 +58,8 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
}
//! Check if the user provided a valid feeRate
-static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector<std::string>& errors) {
+static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector<bilingual_str>& errors)
+{
// check that fee rate is higher than mempool's minimum fee
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
// This may occur if the user set fee_rate or paytxfee too low, if fallbackfee is too low, or, perhaps,
@@ -67,7 +69,7 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
if (newFeerate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
errors.push_back(strprintf(
- "New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- ",
+ Untranslated("New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "),
FormatMoney(newFeerate.GetFeePerK()),
FormatMoney(minMempoolFeeRate.GetFeePerK())));
return feebumper::Result::WALLET_ERROR;
@@ -86,14 +88,14 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
CAmount minTotalFee = nOldFeeRate.GetFee(maxTxSize) + incrementalRelayFee.GetFee(maxTxSize);
if (new_total_fee < minTotalFee) {
- errors.push_back(strprintf("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)",
+ errors.push_back(strprintf(Untranslated("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)"),
FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxTxSize)), FormatMoney(incrementalRelayFee.GetFee(maxTxSize))));
return feebumper::Result::INVALID_PARAMETER;
}
CAmount requiredFee = GetRequiredFee(wallet, maxTxSize);
if (new_total_fee < requiredFee) {
- errors.push_back(strprintf("Insufficient total fee (cannot be less than required fee %s)",
+ errors.push_back(strprintf(Untranslated("Insufficient total fee (cannot be less than required fee %s)"),
FormatMoney(requiredFee)));
return feebumper::Result::INVALID_PARAMETER;
}
@@ -101,8 +103,8 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
// Check that in all cases the new fee doesn't violate maxTxFee
const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
if (new_total_fee > max_tx_fee) {
- errors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)",
- FormatMoney(new_total_fee), FormatMoney(max_tx_fee)));
+ errors.push_back(strprintf(Untranslated("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)"),
+ FormatMoney(new_total_fee), FormatMoney(max_tx_fee)));
return feebumper::Result::WALLET_ERROR;
}
@@ -140,28 +142,26 @@ namespace feebumper {
bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
{
- auto locked_chain = wallet.chain().lock();
LOCK(wallet.cs_wallet);
const CWalletTx* wtx = wallet.GetWalletTx(txid);
if (wtx == nullptr) return false;
- std::vector<std::string> errors_dummy;
+ std::vector<bilingual_str> errors_dummy;
feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
return res == feebumper::Result::OK;
}
-Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<std::string>& errors,
+Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
{
// We are going to modify coin control later, copy to re-use
CCoinControl new_coin_control(coin_control);
- auto locked_chain = wallet.chain().lock();
LOCK(wallet.cs_wallet);
errors.clear();
auto it = wallet.mapWallet.find(txid);
if (it == wallet.mapWallet.end()) {
- errors.push_back("Invalid or non-wallet transaction id");
+ errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
return Result::INVALID_ADDRESS_OR_KEY;
}
const CWalletTx& wtx = it->second;
@@ -218,9 +218,9 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
CTransactionRef tx_new = MakeTransactionRef();
CAmount fee_ret;
int change_pos_in_out = -1; // No requested location for change
- std::string fail_reason;
- if (!wallet.CreateTransaction(*locked_chain, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
- errors.push_back("Unable to create transaction: " + fail_reason);
+ bilingual_str fail_reason;
+ if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
+ errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
return Result::WALLET_ERROR;
}
@@ -240,21 +240,19 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) {
- auto locked_chain = wallet.chain().lock();
LOCK(wallet.cs_wallet);
return wallet.SignTransaction(mtx);
}
-Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid)
+Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<bilingual_str>& errors, uint256& bumped_txid)
{
- auto locked_chain = wallet.chain().lock();
LOCK(wallet.cs_wallet);
if (!errors.empty()) {
return Result::MISC_ERROR;
}
auto it = txid.IsNull() ? wallet.mapWallet.end() : wallet.mapWallet.find(txid);
if (it == wallet.mapWallet.end()) {
- errors.push_back("Invalid or non-wallet transaction id");
+ errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
return Result::MISC_ERROR;
}
CWalletTx& oldWtx = it->second;
@@ -279,7 +277,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
// along with an exception. It would be good to return information about
// wtxBumped to the caller even if marking the original transaction
// replaced does not succeed for some reason.
- errors.push_back("Created new bumpfee transaction but could not mark the original transaction as replaced");
+ errors.push_back(Untranslated("Created new bumpfee transaction but could not mark the original transaction as replaced"));
}
return Result::OK;
}
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index fc038ae731..50577c9d3e 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -12,6 +12,7 @@ class CWalletTx;
class uint256;
class CCoinControl;
enum class FeeEstimateMode;
+struct bilingual_str;
namespace feebumper {
@@ -30,12 +31,12 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid);
//! Create bumpfee transaction based on feerate estimates.
Result CreateRateBumpTransaction(CWallet& wallet,
- const uint256& txid,
- const CCoinControl& coin_control,
- std::vector<std::string>& errors,
- CAmount& old_fee,
- CAmount& new_fee,
- CMutableTransaction& mtx);
+ const uint256& txid,
+ const CCoinControl& coin_control,
+ std::vector<bilingual_str>& errors,
+ CAmount& old_fee,
+ CAmount& new_fee,
+ CMutableTransaction& mtx);
//! Sign the new transaction,
//! @return false if the tx couldn't be found or if it was
@@ -47,10 +48,10 @@ bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx);
//! but sets errors if the tx could not be added to the mempool (will try later)
//! or if the old transaction could not be marked as replaced.
Result CommitTransaction(CWallet& wallet,
- const uint256& txid,
- CMutableTransaction&& mtx,
- std::vector<std::string>& errors,
- uint256& bumped_txid);
+ const uint256& txid,
+ CMutableTransaction&& mtx,
+ std::vector<bilingual_str>& errors,
+ uint256& bumped_txid);
} // namespace feebumper
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 0afd2dfcf0..217a950457 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -53,12 +53,14 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
return false;
}
- std::string error_string;
- std::vector<std::string> warnings;
+ bilingual_str error_string;
+ std::vector<bilingual_str> warnings;
bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warnings);
- if (!error_string.empty()) chain.initError(error_string);
- if (!warnings.empty()) chain.initWarning(Join(warnings, "\n"));
- if (!verify_success) return false;
+ if (!warnings.empty()) chain.initWarning(Join(warnings, "\n", OpTranslated));
+ if (!verify_success) {
+ chain.initError(error_string.translated);
+ return false;
+ }
}
return true;
@@ -68,12 +70,12 @@ bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& walle
{
try {
for (const std::string& walletFile : wallet_files) {
- std::string error;
- std::vector<std::string> warnings;
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error, warnings);
- if (!warnings.empty()) chain.initWarning(Join(warnings, "\n"));
+ if (!warnings.empty()) chain.initWarning(Join(warnings, "\n", OpTranslated));
if (!pwallet) {
- chain.initError(error);
+ chain.initError(error.translated);
return false;
}
AddWallet(pwallet);
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 9417e2bd58..c863d22530 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -133,7 +133,6 @@ UniValue importprivkey(const JSONRPCRequest& request)
WalletRescanReserver reserver(*pwallet);
bool fRescan = true;
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -285,7 +284,6 @@ UniValue importaddress(const JSONRPCRequest& request)
fP2SH = request.params[3].get_bool();
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -317,7 +315,6 @@ UniValue importaddress(const JSONRPCRequest& request)
{
RescanWallet(*pwallet, reserver);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions();
}
@@ -361,7 +358,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
}
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
int height;
if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
@@ -407,7 +403,6 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -487,7 +482,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
std::set<CScript> script_pub_keys;
@@ -505,7 +499,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
{
RescanWallet(*pwallet, reserver);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions();
}
@@ -557,7 +550,6 @@ UniValue importwallet(const JSONRPCRequest& request)
int64_t nTimeBegin = 0;
bool fGood = true;
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -700,7 +692,6 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
- auto locked_chain = pwallet->chain().lock();
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
EnsureWalletIsUnlocked(pwallet);
@@ -756,7 +747,6 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
EnsureWalletIsUnlocked(&wallet);
@@ -780,7 +770,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
std::map<CKeyID, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
- pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth);
+ pwallet->GetKeyBirthTimes(mapKeyBirth);
std::set<CScriptID> scripts = spk_man.GetCScripts();
@@ -1379,7 +1369,6 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
int64_t nLowestTimestamp = 0;
UniValue response(UniValue::VARR);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -1414,7 +1403,6 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
if (fRescan && fRunScan && requests.size()) {
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions();
}
@@ -1676,7 +1664,6 @@ UniValue importdescriptors(const JSONRPCRequest& main_request) {
bool rescan = false;
UniValue response(UniValue::VARR);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -1705,7 +1692,6 @@ UniValue importdescriptors(const JSONRPCRequest& main_request) {
if (rescan) {
int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, true /* update */);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions();
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 49c583c422..e666d55e11 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -23,6 +23,7 @@
#include <util/moneystr.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/translation.h>
#include <util/url.h>
#include <util/vector.h>
#include <wallet/coincontrol.h>
@@ -133,7 +134,7 @@ LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_cr
return *spk_man;
}
-static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
+static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniValue& entry)
{
int confirms = wtx.GetDepthInMainChain();
entry.pushKV("confirmations", confirms);
@@ -148,7 +149,7 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(block_time)));
entry.pushKV("blocktime", block_time);
} else {
- entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
+ entry.pushKV("trusted", wtx.IsTrusted());
}
uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex());
@@ -322,7 +323,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
}
-static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
+static CTransactionRef SendMoney(CWallet* const pwallet, const CTxDestination& address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
CAmount curBalance = pwallet->GetBalance(0, coin_control.m_avoid_address_reuse).m_mine_trusted;
@@ -338,16 +339,16 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
// Create and send the transaction
CAmount nFeeRequired = 0;
- std::string strError;
+ bilingual_str error;
std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CTransactionRef tx;
- if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coin_control)) {
+ if (!pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, error, coin_control)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
- strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
- throw JSONRPCError(RPC_WALLET_ERROR, strError);
+ error = strprintf(Untranslated("Error: This transaction requires a transaction fee of at least %s"), FormatMoney(nFeeRequired));
+ throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
return tx;
@@ -399,7 +400,6 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -445,7 +445,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
+ CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
return tx->GetHash().GetHex();
}
@@ -487,11 +487,10 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
- std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(*locked_chain);
+ std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination& address : grouping)
@@ -543,7 +542,6 @@ static UniValue signmessage(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -572,7 +570,7 @@ static UniValue signmessage(const JSONRPCRequest& request)
return signature;
}
-static CAmount GetReceived(interfaces::Chain::Lock& locked_chain, const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
std::set<CTxDestination> address_set;
@@ -602,7 +600,7 @@ static CAmount GetReceived(interfaces::Chain::Lock& locked_chain, const CWallet&
CAmount amount = 0;
for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
const CWalletTx& wtx = wtx_pair.second;
- if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx) || wtx.GetDepthInMainChain() < min_depth) {
+ if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx) || wtx.GetDepthInMainChain() < min_depth) {
continue;
}
@@ -652,10 +650,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ValueFromAmount(GetReceived(*locked_chain, *pwallet, request.params, /* by_label */ false));
+ return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ false));
}
@@ -693,10 +690,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ValueFromAmount(GetReceived(*locked_chain, *pwallet, request.params, /* by_label */ true));
+ return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ true));
}
@@ -736,7 +732,6 @@ static UniValue getbalance(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
const UniValue& dummy_value = request.params[0];
@@ -778,7 +773,6 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
@@ -841,7 +835,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
@@ -911,11 +904,11 @@ static UniValue sendmany(const JSONRPCRequest& request)
// Send
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
- std::string strFailReason;
+ bilingual_str error;
CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strFailReason, coin_control);
+ bool fCreated = pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, error, coin_control);
if (!fCreated)
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
+ throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
return tx->GetHash().GetHex();
}
@@ -963,7 +956,6 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
- auto locked_chain = pwallet->chain().lock();
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
std::string label;
@@ -1016,7 +1008,7 @@ struct tallyitem
}
};
-static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWallet* const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ListReceived(const CWallet* const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -1049,7 +1041,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWalle
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx)) {
+ if (wtx.IsCoinBase() || !pwallet->chain().checkFinalTx(*wtx.tx)) {
continue;
}
@@ -1209,10 +1201,9 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ListReceived(*locked_chain, pwallet, request.params, false);
+ return ListReceived(pwallet, request.params, false);
}
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
@@ -1254,10 +1245,9 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ListReceived(*locked_chain, pwallet, request.params, true);
+ return ListReceived(pwallet, request.params, true);
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@@ -1278,7 +1268,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -1307,7 +1297,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
- WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
+ WalletTxToJSON(pwallet->chain(), wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
@@ -1349,7 +1339,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
}
entry.pushKV("vout", r.vout);
if (fLong)
- WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
+ WalletTxToJSON(pwallet->chain(), wtx, entry);
ret.push_back(entry);
}
}
@@ -1464,7 +1454,6 @@ UniValue listtransactions(const JSONRPCRequest& request)
UniValue ret(UniValue::VARR);
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
@@ -1473,7 +1462,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second;
- ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter, filter_label);
+ ListTransactions(pwallet, *pwtx, 0, true, ret, filter, filter_label);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
}
@@ -1557,7 +1546,6 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
// The way the 'height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
@@ -1598,7 +1586,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
CWalletTx tx = pairWtx.second;
if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
- ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
+ ListTransactions(pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1615,7 +1603,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
if (it != pwallet->mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
+ ListTransactions(pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
}
}
blockId = block.hashPrevBlock;
@@ -1700,7 +1688,6 @@ static UniValue gettransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -1729,10 +1716,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
if (wtx.IsFromMe(filter))
entry.pushKV("fee", ValueFromAmount(nFee));
- WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
+ WalletTxToJSON(pwallet->chain(), wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
+ ListTransactions(pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
@@ -1776,7 +1763,6 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -1817,7 +1803,6 @@ static UniValue backupwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
std::string strDest = request.params[0].get_str();
@@ -1855,7 +1840,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
@@ -1909,7 +1893,6 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
int64_t nSleepTime;
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -1991,7 +1974,6 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2047,7 +2029,6 @@ static UniValue walletlock(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2094,7 +2075,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -2173,7 +2153,6 @@ static UniValue lockunspent(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
@@ -2286,7 +2265,6 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
@@ -2329,7 +2307,6 @@ static UniValue settxfee(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]);
@@ -2388,7 +2365,6 @@ static UniValue getbalances(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
- auto locked_chain = wallet.chain().lock();
LOCK(wallet.cs_wallet);
const auto bal = wallet.GetBalance();
@@ -2465,7 +2441,6 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ);
@@ -2615,14 +2590,14 @@ static UniValue loadwallet(const JSONRPCRequest& request)
}
}
- std::string error;
- std::vector<std::string> warning;
- std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_chain, location, error, warning);
- if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error);
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
+ std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_chain, location, error, warnings);
+ if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
- obj.pushKV("warning", Join(warning, "\n"));
+ obj.pushKV("warning", Join(warnings, "\n", OpOriginal));
return obj;
}
@@ -2731,12 +2706,12 @@ static UniValue createwallet(const JSONRPCRequest& request)
}
SecureString passphrase;
passphrase.reserve(100);
- std::vector<std::string> warnings;
+ std::vector<bilingual_str> warnings;
if (!request.params[3].isNull()) {
passphrase = request.params[3].get_str().c_str();
if (passphrase.empty()) {
// Empty string means unencrypted
- warnings.emplace_back("Empty string given as passphrase, wallet will not be encrypted.");
+ warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
}
}
@@ -2747,14 +2722,14 @@ static UniValue createwallet(const JSONRPCRequest& request)
flags |= WALLET_FLAG_DESCRIPTORS;
}
- std::string error;
+ bilingual_str error;
std::shared_ptr<CWallet> wallet;
WalletCreationStatus status = CreateWallet(*g_rpc_chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
switch (status) {
case WalletCreationStatus::CREATION_FAILED:
- throw JSONRPCError(RPC_WALLET_ERROR, error);
+ throw JSONRPCError(RPC_WALLET_ERROR, error.original);
case WalletCreationStatus::ENCRYPTION_FAILED:
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error.original);
case WalletCreationStatus::SUCCESS:
break;
// no default case, so the compiler can warn about missing cases
@@ -2762,7 +2737,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
- obj.pushKV("warning", Join(warnings, "\n"));
+ obj.pushKV("warning", Join(warnings, "\n", OpOriginal));
return obj;
}
@@ -2940,9 +2915,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
cctl.m_avoid_address_reuse = false;
cctl.m_min_depth = nMinDepth;
cctl.m_max_depth = nMaxDepth;
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ pwallet->AvailableCoins(vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
@@ -3135,10 +3109,10 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
setSubtractFeeFromOutputs.insert(pos);
}
- std::string strFailReason;
+ bilingual_str error;
- if (!pwallet->FundTransaction(tx, fee_out, change_position, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
- throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
+ if (!pwallet->FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
}
@@ -3312,7 +3286,6 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
}
// Sign the transaction
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -3441,12 +3414,11 @@ static UniValue bumpfee(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- std::vector<std::string> errors;
+ std::vector<bilingual_str> errors;
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
@@ -3456,19 +3428,19 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0]);
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0].original);
break;
case feebumper::Result::INVALID_REQUEST:
- throw JSONRPCError(RPC_INVALID_REQUEST, errors[0]);
+ throw JSONRPCError(RPC_INVALID_REQUEST, errors[0].original);
break;
case feebumper::Result::INVALID_PARAMETER:
- throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0]);
+ throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0].original);
break;
case feebumper::Result::WALLET_ERROR:
- throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
+ throw JSONRPCError(RPC_WALLET_ERROR, errors[0].original);
break;
default:
- throw JSONRPCError(RPC_MISC_ERROR, errors[0]);
+ throw JSONRPCError(RPC_MISC_ERROR, errors[0].original);
break;
}
}
@@ -3484,7 +3456,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
uint256 txid;
if (feebumper::CommitTransaction(*pwallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
- throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
+ throw JSONRPCError(RPC_WALLET_ERROR, errors[0].original);
}
result.pushKV("txid", txid.GetHex());
@@ -3502,8 +3474,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
result.pushKV("origfee", ValueFromAmount(old_fee));
result.pushKV("fee", ValueFromAmount(new_fee));
UniValue result_errors(UniValue::VARR);
- for (const std::string& error : errors) {
- result_errors.push_back(error);
+ for (const bilingual_str& error : errors) {
+ result_errors.push_back(error.original);
}
result.pushKV("errors", result_errors);
@@ -3548,7 +3520,6 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
Optional<int> stop_height;
uint256 start_block;
{
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
int tip_height = pwallet->GetLastBlockHeight();
@@ -3563,8 +3534,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
stop_height = request.params[1].get_int();
if (*stop_height < 0 || *stop_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
- }
- else if (*stop_height < start_height) {
+ } else if (*stop_height < start_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height");
}
}
@@ -4011,7 +3981,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
}
- auto locked_chain = pwallet->chain().lock();
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
// Do not do anything to non-HD wallets
@@ -4271,12 +4240,12 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
version = request.params[0].get_int();
}
- std::string error;
- std::vector<std::string> warnings;
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
if (!pwallet->UpgradeWallet(version, error, warnings)) {
- throw JSONRPCError(RPC_WALLET_ERROR, error);
+ throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
- return error;
+ return error.original;
}
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index ecb95d599d..e4be5045e1 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -377,10 +377,9 @@ bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const
return keypool_has_keys;
}
-bool LegacyScriptPubKeyMan::Upgrade(int prev_version, std::string& error)
+bool LegacyScriptPubKeyMan::Upgrade(int prev_version, bilingual_str& error)
{
LOCK(cs_KeyStore);
- error = "";
bool hd_upgrade = false;
bool split_upgrade = false;
if (m_storage.CanSupportFeature(FEATURE_HD) && !IsHDEnabled()) {
@@ -405,7 +404,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, std::string& error)
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!TopUp()) {
- error = _("Unable to generate keys").translated;
+ error = _("Unable to generate keys");
return false;
}
}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 3117b13d35..5414fc5923 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -19,6 +19,7 @@
#include <boost/signals2/signal.hpp>
enum class OutputType;
+struct bilingual_str;
// Wallet storage things that ScriptPubKeyMans need in order to be able to store things to the wallet database.
// It provides access to things that are part of the entire wallet and not specific to a ScriptPubKeyMan such as
@@ -191,7 +192,7 @@ public:
virtual bool CanGetAddresses(bool internal = false) const { return false; }
/** Upgrades the wallet to the specified version */
- virtual bool Upgrade(int prev_version, std::string& error) { return false; }
+ virtual bool Upgrade(int prev_version, bilingual_str& error) { return false; }
virtual bool HavePrivateKeys() const { return false; }
@@ -343,7 +344,7 @@ public:
bool SetupGeneration(bool force = false) override;
- bool Upgrade(int prev_version, std::string& error) override;
+ bool Upgrade(int prev_version, bilingual_str& error) override;
bool HavePrivateKeys() const override;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 1215956bb4..0826b88f0a 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -15,6 +15,7 @@
#include <rpc/server.h>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
+#include <util/translation.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
@@ -30,8 +31,8 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
{
- std::string error;
- std::vector<std::string> warnings;
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
auto wallet = CWallet::CreateWalletFromFile(chain, WalletLocation(""), error, warnings);
wallet->postInitProcess();
return wallet;
@@ -75,8 +76,6 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
- auto locked_chain = chain->lock();
- LockAssertion lock(::cs_main);
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
@@ -116,7 +115,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
}
// Prune the older block file.
- PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ {
+ LOCK(cs_main);
+ PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ }
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions only picks transactions in the new block
@@ -139,7 +141,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
}
// Prune the remaining block file.
- PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ {
+ LOCK(cs_main);
+ PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ }
UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions scans no blocks.
@@ -171,11 +176,12 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
- auto locked_chain = chain->lock();
- LockAssertion lock(::cs_main);
// Prune the older block file.
- PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ {
+ LOCK(cs_main);
+ PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ }
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
// Verify importmulti RPC returns failure for a key whose creation time is
@@ -241,8 +247,6 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
- auto locked_chain = chain->lock();
- LockAssertion lock(::cs_main);
std::string backup_file = (GetDataDir() / "wallet.backup").string();
@@ -308,8 +312,6 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back());
- auto locked_chain = chain->lock();
- LockAssertion lock(::cs_main);
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -334,8 +336,6 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
- auto locked_chain = wallet.chain().lock();
- LockAssertion lock(::cs_main);
auto inserted = ::BlockIndex().emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
@@ -345,7 +345,6 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
}
CWalletTx wtx(&wallet, MakeTransactionRef(tx));
- LOCK(cs_main);
LOCK(wallet.cs_wallet);
// If transaction is already in map, to avoid inconsistencies, unconfirmation
// is needed before confirm again with different block.
@@ -492,7 +491,7 @@ public:
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
{
- LOCK2(::cs_main, wallet->cs_wallet);
+ LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
bool firstRun;
@@ -517,11 +516,10 @@ public:
CTransactionRef tx;
CAmount fee;
int changePos = -1;
- std::string error;
+ bilingual_str error;
CCoinControl dummy;
{
- auto locked_chain = m_chain->lock();
- BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, fee, changePos, error, dummy));
+ BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy));
}
wallet->CommitTransaction(tx, {}, {});
CMutableTransaction blocktx;
@@ -531,7 +529,6 @@ public:
}
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- LOCK(cs_main);
LOCK(wallet->cs_wallet);
wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
@@ -554,9 +551,8 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// address.
std::map<CTxDestination, std::vector<COutput>> list;
{
- auto locked_chain = m_chain->lock();
LOCK(wallet->cs_wallet);
- list = wallet->ListCoins(*locked_chain);
+ list = wallet->ListCoins();
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
@@ -571,9 +567,8 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// pubkey.
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{
- auto locked_chain = m_chain->lock();
LOCK(wallet->cs_wallet);
- list = wallet->ListCoins(*locked_chain);
+ list = wallet->ListCoins();
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
@@ -581,10 +576,9 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// Lock both coins. Confirm number of available coins drops to 0.
{
- auto locked_chain = m_chain->lock();
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(*locked_chain, available);
+ wallet->AvailableCoins(available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -594,18 +588,16 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
}
}
{
- auto locked_chain = m_chain->lock();
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(*locked_chain, available);
+ wallet->AvailableCoins(available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
{
- auto locked_chain = m_chain->lock();
LOCK(wallet->cs_wallet);
- list = wallet->ListCoins(*locked_chain);
+ list = wallet->ListCoins();
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
@@ -694,11 +686,20 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup)
//! conditions if it's called the same time an incoming transaction shows up in
//! the mempool or a new block.
//!
-//! It isn't possible for a unit test to totally verify there aren't race
-//! conditions without hooking into the implementation more, so this test just
-//! verifies that new transactions are detected during loading without any
-//! notifications at all, to infer that timing of notifications shouldn't
-//! matter. The test could be extended to cover other scenarios in the future.
+//! It isn't possible to verify there aren't race condition in every case, so
+//! this test just checks two specific cases and ensures that timing of
+//! notifications in these cases doesn't prevent the wallet from detecting
+//! transactions.
+//!
+//! In the first case, block and mempool transactions are created before the
+//! wallet is loaded, but notifications about these transactions are delayed
+//! until after it is loaded. The notifications are superfluous in this case, so
+//! the test verifies the transactions are detected before they arrive.
+//!
+//! In the second case, block and mempool transactions are created after the
+//! wallet rescan and notifications are immediately synced, to verify the wallet
+//! must already have a handler in place for them, and there's no gap after
+//! rescanning where new transactions in new blocks could be lost.
BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
{
// Create new wallet with known key and unload it.
@@ -709,6 +710,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
AddKey(*wallet, key);
TestUnloadWallet(std::move(wallet));
+
// Add log hook to detect AddToWallet events from rescans, blockConnected,
// and transactionAddedToMempool notifications
int addtx_count = 0;
@@ -717,21 +719,14 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
return false;
});
+
bool rescan_completed = false;
DebugLogHelper rescan_check("[default wallet] Rescan completed", [&](const std::string* s) {
- if (s) {
- // For now, just assert that cs_main is being held during the
- // rescan, ensuring that a new block couldn't be connected
- // that the wallet would miss. After
- // https://github.com/bitcoin/bitcoin/pull/16426 when cs_main is no
- // longer held, the test can be extended to append a new block here
- // and check it's handled correctly.
- AssertLockHeld(::cs_main);
- rescan_completed = true;
- }
+ if (s) rescan_completed = true;
return false;
});
+
// Block the queue to prevent the wallet receiving blockConnected and
// transactionAddedToMempool notifications, and create block and mempool
// transactions paying to the wallet
@@ -746,29 +741,56 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
BOOST_CHECK(chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
+
// Reload wallet and make sure new transactions are detected despite events
// being blocked
wallet = TestLoadWallet(*chain);
BOOST_CHECK(rescan_completed);
BOOST_CHECK_EQUAL(addtx_count, 2);
- unsigned int block_tx_time, mempool_tx_time;
{
LOCK(wallet->cs_wallet);
- block_tx_time = wallet->mapWallet.at(block_tx.GetHash()).nTimeReceived;
- mempool_tx_time = wallet->mapWallet.at(mempool_tx.GetHash()).nTimeReceived;
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1);
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1);
}
+
// Unblock notification queue and make sure stale blockConnected and
// transactionAddedToMempool events are processed
promise.set_value();
SyncWithValidationInterfaceQueue();
BOOST_CHECK_EQUAL(addtx_count, 4);
+
+
+ TestUnloadWallet(std::move(wallet));
+
+
+ // Load wallet again, this time creating new block and mempool transactions
+ // paying to the wallet as the wallet finishes loading and syncing the
+ // queue so the events have to be handled immediately. Releasing the wallet
+ // lock during the sync is a little artificial but is needed to avoid a
+ // deadlock during the sync and simulates a new block notification happening
+ // as soon as possible.
+ addtx_count = 0;
+ auto handler = HandleLoadWallet([&](std::unique_ptr<interfaces::Wallet> wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->wallet()->cs_wallet) {
+ BOOST_CHECK(rescan_completed);
+ m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
+ m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
+ BOOST_CHECK(chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
+ LEAVE_CRITICAL_SECTION(wallet->wallet()->cs_wallet);
+ SyncWithValidationInterfaceQueue();
+ ENTER_CRITICAL_SECTION(wallet->wallet()->cs_wallet);
+ });
+ wallet = TestLoadWallet(*chain);
+ BOOST_CHECK_EQUAL(addtx_count, 4);
{
LOCK(wallet->cs_wallet);
- BOOST_CHECK_EQUAL(block_tx_time, wallet->mapWallet.at(block_tx.GetHash()).nTimeReceived);
- BOOST_CHECK_EQUAL(mempool_tx_time, wallet->mapWallet.at(mempool_tx.GetHash()).nTimeReceived);
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1);
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1);
}
+
TestUnloadWallet(std::move(wallet));
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6d46841cee..a893548971 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -27,6 +27,7 @@
#include <util/fees.h>
#include <util/moneystr.h>
#include <util/rbf.h>
+#include <util/string.h>
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
@@ -149,34 +150,34 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector<std::string>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
if (!CWallet::Verify(chain, location, false, error, warnings)) {
- error = "Wallet file verification failed: " + error;
+ error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr;
}
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings);
if (!wallet) {
- error = "Wallet loading failed: " + error;
+ error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
return nullptr;
}
AddWallet(wallet);
wallet->postInitProcess();
return wallet;
} catch (const std::runtime_error& e) {
- error = e.what();
+ error = Untranslated(e.what());
return nullptr;
}
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::vector<std::string>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
return LoadWallet(chain, WalletLocation(name), error, warnings);
}
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::shared_ptr<CWallet>& result)
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
{
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
@@ -189,39 +190,39 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
// Check the wallet file location
WalletLocation location(name);
if (location.Exists()) {
- error = "Wallet " + location.GetName() + " already exists.";
+ error = strprintf(Untranslated("Wallet %s already exists."), location.GetName());
return WalletCreationStatus::CREATION_FAILED;
}
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
if (!CWallet::Verify(chain, location, false, error, warnings)) {
- error = "Wallet file verification failed: " + error;
+ error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED;
}
// Do not allow a passphrase when private keys are disabled
if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- error = "Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.";
+ error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
return WalletCreationStatus::CREATION_FAILED;
}
// Make the wallet
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings, wallet_creation_flags);
if (!wallet) {
- error = "Wallet creation failed: " + error;
+ error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED;
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
- error = "Error: Wallet created but failed to encrypt.";
+ error = Untranslated("Error: Wallet created but failed to encrypt.");
return WalletCreationStatus::ENCRYPTION_FAILED;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
- error = "Error: Wallet was encrypted but could not be unlocked";
+ error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
return WalletCreationStatus::ENCRYPTION_FAILED;
}
@@ -233,7 +234,7 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
} else {
for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
- error = "Unable to generate initial keys";
+ error = Untranslated("Unable to generate initial keys");
return WalletCreationStatus::CREATION_FAILED;
}
}
@@ -885,10 +886,9 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
void CWallet::LoadToWallet(CWalletTx& wtxIn)
{
- // If wallet doesn't have a chain (e.g bitcoin-wallet), lock can't be taken.
- auto locked_chain = LockChain();
- if (locked_chain) {
- Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock);
+ // If wallet doesn't have a chain (e.g wallet-tool), don't bother to update txn.
+ if (HaveChain()) {
+ Optional<int> block_height = chain().getBlockHeight(wtxIn.m_confirm.hashBlock);
if (block_height) {
// Update cached block height variable since it not stored in the
// serialized transaction.
@@ -975,7 +975,6 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
@@ -993,7 +992,6 @@ void CWallet::MarkInputsDirty(const CTransactionRef& tx)
bool CWallet::AbandonTransaction(const uint256& hashTx)
{
- auto locked_chain = chain().lock(); // Temporary. Removed in upcoming lock cleanup
LOCK(cs_wallet);
WalletBatch batch(*database, "r+");
@@ -1048,7 +1046,6 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1;
@@ -1111,7 +1108,6 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
}
void CWallet::transactionAddedToMempool(const CTransactionRef& ptx) {
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
SyncTransaction(ptx, confirm);
@@ -1133,7 +1129,6 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef &ptx) {
void CWallet::blockConnected(const CBlock& block, int height)
{
const uint256& block_hash = block.GetHash();
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
m_last_block_processed_height = height;
@@ -1147,7 +1142,6 @@ void CWallet::blockConnected(const CBlock& block, int height)
void CWallet::blockDisconnected(const CBlock& block, int height)
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
// At block disconnection, this will change an abandoned transaction to
@@ -1686,7 +1680,6 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
uint256 next_block_hash;
bool reorg = false;
if (chain().findBlock(block_hash, FoundBlock().data(block)) && !block.IsNull()) {
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
next_block = chain().findNextBlock(block_hash, block_height, FoundBlock().hash(next_block_hash), &reorg);
if (reorg) {
@@ -1715,7 +1708,6 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
{
- auto locked_chain = chain().lock();
if (!next_block || reorg) {
// break successfully when rescan has reached the tip, or
// previous block is no longer on the chain due to a reorg
@@ -1928,16 +1920,16 @@ bool CWalletTx::InMempool() const
return fInMempool;
}
-bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
+bool CWalletTx::IsTrusted() const
{
std::set<uint256> s;
- return IsTrusted(locked_chain, s);
+ return IsTrusted(s);
}
-bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain, std::set<uint256>& trusted_parents) const
+bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
{
// Quick answer in most cases
- if (!locked_chain.checkFinalTx(*tx)) return false;
+ if (!pwallet->chain().checkFinalTx(*tx)) return false;
int nDepth = GetDepthInMainChain();
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
@@ -1959,7 +1951,7 @@ bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain, std::set<uint25
// If we've already trusted this parent, continue
if (trusted_parents.count(parent->GetHash())) continue;
// Recurse to check that the parent is also trusted
- if (!parent->IsTrusted(locked_chain, trusted_parents)) return false;
+ if (!parent->IsTrusted(trusted_parents)) return false;
trusted_parents.insert(parent->GetHash());
}
return true;
@@ -2003,8 +1995,7 @@ void CWallet::ResendWalletTransactions()
int submitted_tx_count = 0;
- { // locked_chain and cs_wallet scope
- auto locked_chain = chain().lock();
+ { // cs_wallet scope
LOCK(cs_wallet);
// Relay transactions
@@ -2017,7 +2008,7 @@ void CWallet::ResendWalletTransactions()
std::string unused_err_string;
if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count;
}
- } // locked_chain and cs_wallet
+ } // cs_wallet
if (submitted_tx_count > 0) {
WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, submitted_tx_count);
@@ -2045,13 +2036,12 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
Balance ret;
isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
std::set<uint256> trusted_parents;
for (const auto& entry : mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{wtx.IsTrusted(*locked_chain, trusted_parents)};
+ const bool is_trusted{wtx.IsTrusted(trusted_parents)};
const int tx_depth{wtx.GetDepthInMainChain()};
const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
@@ -2072,12 +2062,11 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
CAmount balance = 0;
std::vector<COutput> vCoins;
- AvailableCoins(*locked_chain, vCoins, true, coinControl);
+ AvailableCoins(vCoins, true, coinControl);
for (const COutput& out : vCoins) {
if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue;
@@ -2086,7 +2075,7 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance;
}
-void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const
+void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const
{
AssertLockHeld(cs_wallet);
@@ -2104,7 +2093,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
const uint256& wtxid = entry.first;
const CWalletTx& wtx = entry.second;
- if (!locked_chain.checkFinalTx(*wtx.tx)) {
+ if (!chain().checkFinalTx(*wtx.tx)) {
continue;
}
@@ -2120,7 +2109,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = wtx.IsTrusted(locked_chain, trusted_parents);
+ bool safeTx = wtx.IsTrusted(trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2208,14 +2197,14 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
}
}
-std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Chain::Lock& locked_chain) const
+std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
{
AssertLockHeld(cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
- AvailableCoins(locked_chain, availableCoins);
+ AvailableCoins(availableCoins);
for (const COutput& coin : availableCoins) {
CTxDestination address;
@@ -2532,7 +2521,7 @@ SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkh
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
{
std::vector<CRecipient> vecSend;
@@ -2551,11 +2540,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
// Acquire the locks to prevent races to the new locked unspents between the
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
CTransactionRef tx_new;
- if (!CreateTransaction(*locked_chain, vecSend, tx_new, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
+ if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, false)) {
return false;
}
@@ -2671,8 +2659,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
return m_default_address_type;
}
-bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet,
- int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
+bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
@@ -2683,7 +2670,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
{
if (nValue < 0 || recipient.nAmount < 0)
{
- strFailReason = _("Transaction amounts must not be negative").translated;
+ error = _("Transaction amounts must not be negative");
return false;
}
nValue += recipient.nAmount;
@@ -2693,7 +2680,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
if (vecSend.empty())
{
- strFailReason = _("Transaction must have at least one recipient").translated;
+ error = _("Transaction must have at least one recipient");
return false;
}
@@ -2703,12 +2690,11 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
int nBytes;
{
std::set<CInputCoin> setCoins;
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
{
std::vector<COutput> vAvailableCoins;
- AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
+ AvailableCoins(vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
// Create change script that will be used if we need change
@@ -2731,7 +2717,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// destination in case we don't need change.
CTxDestination dest;
if (!reservedest.GetReservedDestination(dest, true)) {
- strFailReason = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.").translated;
+ error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
}
scriptChange = GetScriptForDestination(dest);
assert(!dest.empty() || scriptChange.empty());
@@ -2793,12 +2779,12 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
if (txout.nValue < 0)
- strFailReason = _("The transaction amount is too small to pay the fee").translated;
+ error = _("The transaction amount is too small to pay the fee");
else
- strFailReason = _("The transaction amount is too small to send after the fee has been deducted").translated;
+ error = _("The transaction amount is too small to send after the fee has been deducted");
}
else
- strFailReason = _("Transaction amount too small").translated;
+ error = _("Transaction amount too small");
return false;
}
txNew.vout.push_back(txout);
@@ -2826,7 +2812,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
continue;
}
else {
- strFailReason = _("Insufficient funds").translated;
+ error = _("Insufficient funds");
return false;
}
}
@@ -2857,7 +2843,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
{
- strFailReason = _("Change index out of range").translated;
+ error = _("Change index out of range");
return false;
}
@@ -2876,14 +2862,14 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
if (nBytes < 0) {
- strFailReason = _("Signing transaction failed").translated;
+ error = _("Signing transaction failed");
return false;
}
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
- strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.").translated;
+ error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
}
@@ -2923,7 +2909,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// fee to pay for the new output and still meet nFeeNeeded
// Or we should have just subtracted fee from recipients and
// nFeeNeeded should not have changed
- strFailReason = _("Transaction fee and change calculation failed").translated;
+ error = _("Transaction fee and change calculation failed");
return false;
}
@@ -2976,7 +2962,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
if (sign && !SignTransaction(txNew)) {
- strFailReason = _("Signing transaction failed").translated;
+ error = _("Signing transaction failed");
return false;
}
@@ -2986,20 +2972,20 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// Limit size
if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)
{
- strFailReason = _("Transaction too large").translated;
+ error = _("Transaction too large");
return false;
}
}
if (nFeeRet > m_default_max_tx_fee) {
- strFailReason = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
+ error = Untranslated(TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED));
return false;
}
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
if (!chain().checkChainLimits(tx)) {
- strFailReason = _("Transaction has too long of a mempool chain").translated;
+ error = _("Transaction has too long of a mempool chain");
return false;
}
}
@@ -3021,7 +3007,6 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm)
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
CWalletTx wtxNew(this, std::move(tx));
@@ -3061,11 +3046,6 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
- // Even if we don't use this lock in this function, we want to preserve
- // lock order in LoadToWallet if query of chain state is needed to know
- // tx status. If lock can't be taken (e.g bitcoin-wallet), tx confirmation
- // status may be not reliable.
- auto locked_chain = LockChain();
LOCK(cs_wallet);
fFirstRunRet = false;
@@ -3284,7 +3264,7 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations
}
}
-std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain) const
+std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
{
std::map<CTxDestination, CAmount> balances;
@@ -3295,7 +3275,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
{
const CWalletTx& wtx = walletEntry.second;
- if (!wtx.IsTrusted(locked_chain, trusted_parents))
+ if (!wtx.IsTrusted(trusted_parents))
continue;
if (wtx.IsImmatureCoinBase())
@@ -3511,7 +3491,7 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CKeyID, int64_t>& mapKeyBirth) const {
+void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
@@ -3677,7 +3657,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::vector<std::string>& warnings)
+bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
{
// Do some checking on wallet path. It should be either a:
//
@@ -3691,17 +3671,17 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
(path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
- error_string = strprintf(
+ error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
- location.GetName(), GetWalletDir());
+ location.GetName(), GetWalletDir()));
return false;
}
// Make sure that the wallet path doesn't clash with an existing wallet path
if (IsWalletLoaded(wallet_path)) {
- error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName());
+ error_string = Untranslated(strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName()));
return false;
}
@@ -3713,7 +3693,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
return false;
}
} catch (const fs::filesystem_error& e) {
- error_string = strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e));
+ error_string = Untranslated(strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e)));
return false;
}
@@ -3721,11 +3701,6 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// Recover readable keypairs:
CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename;
- // Even if we don't use this lock in this function, we want to preserve
- // lock order in LoadToWallet if query of chain state is needed to know
- // tx status. If lock can't be taken, tx confirmation status may be not
- // reliable.
- auto locked_chain = dummyWallet.LockChain();
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false;
}
@@ -3734,7 +3709,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string);
}
-std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector<std::string>& warnings, uint64_t wallet_creation_flags)
+std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags)
{
const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
@@ -3747,7 +3722,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
- error = strprintf(_("Error loading %s: Wallet corrupted").translated, walletFile);
+ error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
return nullptr;
}
}
@@ -3762,26 +3737,26 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
- error = strprintf(_("Error loading %s: Wallet corrupted").translated, walletFile);
+ error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
{
warnings.push_back(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
- " or address book entries might be missing or incorrect.").translated,
+ " or address book entries might be missing or incorrect."),
walletFile));
}
else if (nLoadWalletRet == DBErrors::TOO_NEW) {
- error = strprintf(_("Error loading %s: Wallet requires newer version of %s").translated, walletFile, PACKAGE_NAME);
+ error = strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, PACKAGE_NAME);
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
- error = strprintf(_("Wallet needed to be rewritten: restart %s to complete").translated, PACKAGE_NAME);
+ error = strprintf(_("Wallet needed to be rewritten: restart %s to complete"), PACKAGE_NAME);
return nullptr;
}
else {
- error = strprintf(_("Error loading %s").translated, walletFile);
+ error = strprintf(_("Error loading %s"), walletFile);
return nullptr;
}
}
@@ -3807,47 +3782,46 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Legacy wallets need SetupGeneration here.
for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
- error = _("Unable to generate initial keys").translated;
+ error = _("Unable to generate initial keys");
return nullptr;
}
}
}
}
- auto locked_chain = chain.lock();
- walletInstance->chainStateFlushed(locked_chain->getTipLocator());
+ walletInstance->chainStateFlushed(chain.getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
- error = strprintf(_("Error loading %s: Private keys can only be disabled during creation").translated, walletFile);
+ error = strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile);
return NULL;
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
if (spk_man->HavePrivateKeys()) {
- warnings.push_back(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys").translated, walletFile));
+ warnings.push_back(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
break;
}
}
}
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
- error = strprintf(_("Unknown address type '%s'").translated, gArgs.GetArg("-addresstype", ""));
+ error = strprintf(_("Unknown address type '%s'"), gArgs.GetArg("-addresstype", ""));
return nullptr;
}
if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
- error = strprintf(_("Unknown change type '%s'").translated, gArgs.GetArg("-changetype", ""));
+ error = strprintf(_("Unknown change type '%s'"), gArgs.GetArg("-changetype", ""));
return nullptr;
}
if (gArgs.IsArgSet("-mintxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) {
- error = AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")).translated;
+ error = AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""));
return nullptr;
}
if (n > HIGH_TX_FEE_PER_KB) {
- warnings.push_back(AmountHighWarn("-mintxfee").translated + " " +
- _("This is the minimum transaction fee you pay on every transaction.").translated);
+ warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") +
+ _("This is the minimum transaction fee you pay on every transaction."));
}
walletInstance->m_min_fee = CFeeRate(n);
}
@@ -3855,12 +3829,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-fallbackfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
- error = strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'").translated, gArgs.GetArg("-fallbackfee", ""));
+ error = strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", ""));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- warnings.push_back(AmountHighWarn("-fallbackfee").translated + " " +
- _("This is the transaction fee you may pay when fee estimates are not available.").translated);
+ warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") +
+ _("This is the transaction fee you may pay when fee estimates are not available."));
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
}
@@ -3870,28 +3844,28 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-discardfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) {
- error = strprintf(_("Invalid amount for -discardfee=<amount>: '%s'").translated, gArgs.GetArg("-discardfee", ""));
+ error = strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", ""));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- warnings.push_back(AmountHighWarn("-discardfee").translated + " " +
- _("This is the transaction fee you may discard if change is smaller than dust at this level").translated);
+ warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
}
walletInstance->m_discard_rate = CFeeRate(nFeePerK);
}
if (gArgs.IsArgSet("-paytxfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
- error = AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")).translated;
+ error = AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- warnings.push_back(AmountHighWarn("-paytxfee").translated + " " +
- _("This is the transaction fee you will pay if you send a transaction.").translated);
+ warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") +
+ _("This is the transaction fee you will pay if you send a transaction."));
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) {
- error = strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)").translated,
+ error = strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString());
return nullptr;
}
@@ -3900,23 +3874,23 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-maxtxfee")) {
CAmount nMaxFee = 0;
if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) {
- error = AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")).translated;
+ error = AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""));
return nullptr;
}
if (nMaxFee > HIGH_MAX_TX_FEE) {
- warnings.push_back(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.").translated);
+ warnings.push_back(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
}
if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) {
- error = strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)").translated,
- gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString());
+ error = strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
+ gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString());
return nullptr;
}
walletInstance->m_default_max_tx_fee = nMaxFee;
}
if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) {
- warnings.push_back(AmountHighWarn("-minrelaytxfee").translated + " " +
- _("The wallet will avoid paying less than the minimum relay fee.").translated);
+ warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") +
+ _("The wallet will avoid paying less than the minimum relay fee."));
}
walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
@@ -3928,24 +3902,33 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
- auto locked_chain = chain.lock();
LOCK(walletInstance->cs_wallet);
+ // Register wallet with validationinterface. It's done before rescan to avoid
+ // missing block connections between end of rescan and validation subscribing.
+ // Because of wallet lock being hold, block connection notifications are going to
+ // be pending on the validation-side until lock release. It's likely to have
+ // block processing duplicata (if rescan block range overlaps with notification one)
+ // but we guarantee at least than wallet state is correct after notifications delivery.
+ // This is temporary until rescan and notifications delivery are unified under same
+ // interface.
+ walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
+
int rescan_height = 0;
if (!gArgs.GetBoolArg("-rescan", false))
{
WalletBatch batch(*walletInstance->database);
CBlockLocator locator;
if (batch.ReadBestBlock(locator)) {
- if (const Optional<int> fork_height = locked_chain->findLocatorFork(locator)) {
+ if (const Optional<int> fork_height = chain.findLocatorFork(locator)) {
rescan_height = *fork_height;
}
}
}
- const Optional<int> tip_height = locked_chain->getHeight();
+ const Optional<int> tip_height = chain.getHeight();
if (tip_height) {
- walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height);
+ walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height);
walletInstance->m_last_block_processed_height = *tip_height;
} else {
walletInstance->m_last_block_processed.SetNull();
@@ -3962,12 +3945,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// If a block is pruned after this check, we will load the wallet,
// but fail the rescan with a generic error.
int block_height = *tip_height;
- while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
+ while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
--block_height;
}
if (rescan_height != block_height) {
- error = _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)").translated;
+ error = _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)");
return nullptr;
}
}
@@ -3984,19 +3967,19 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (!time_first_key || time < *time_first_key) time_first_key = time;
}
if (time_first_key) {
- if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
+ if (Optional<int> first_block = chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
rescan_height = *first_block;
}
}
{
WalletRescanReserver reserver(*walletInstance);
- if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), rescan_height, {} /* max height */, reserver, true /* update */).status)) {
- error = _("Failed to rescan the wallet during initialization").translated;
+ if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, {} /* max height */, reserver, true /* update */).status)) {
+ error = _("Failed to rescan the wallet during initialization");
return nullptr;
}
}
- walletInstance->chainStateFlushed(locked_chain->getTipLocator());
+ walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->database->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
@@ -4031,9 +4014,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
- // Register with the validation interface. It's ok to do this after rescan since we're still holding locked_chain.
- walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
-
walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
{
@@ -4055,7 +4035,7 @@ const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest
return &address_book_it->second;
}
-bool CWallet::UpgradeWallet(int version, std::string& error, std::vector<std::string>& warnings)
+bool CWallet::UpgradeWallet(int version, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
int prev_version = GetVersion();
int nMaxVersion = version;
@@ -4064,12 +4044,12 @@ bool CWallet::UpgradeWallet(int version, std::string& error, std::vector<std::st
WalletLogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
nMaxVersion = FEATURE_LATEST;
SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
- }
- else
+ } else {
WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
+ }
if (nMaxVersion < GetVersion())
{
- error = _("Cannot downgrade wallet").translated;
+ error = _("Cannot downgrade wallet");
return false;
}
SetMaxVersion(nMaxVersion);
@@ -4079,7 +4059,7 @@ bool CWallet::UpgradeWallet(int version, std::string& error, std::vector<std::st
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
int max_version = GetVersion();
if (!CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
- error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified.").translated;
+ error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified.");
return false;
}
@@ -4093,7 +4073,6 @@ bool CWallet::UpgradeWallet(int version, std::string& error, std::vector<std::st
void CWallet::postInitProcess()
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
// Add wallet transactions that aren't already in a block to mempool
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 24c78fceb3..3be3435596 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -40,6 +40,8 @@
using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
+struct bilingual_str;
+
//! Explicitly unload and delete the wallet.
//! Blocks the current thread after signaling the unload intent so that all
//! wallet clients release the wallet.
@@ -52,7 +54,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
bool HasWallets();
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector<std::string>& warnings);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
enum class WalletCreationStatus {
@@ -61,7 +63,7 @@ enum class WalletCreationStatus {
ENCRYPTION_FAILED
};
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::shared_ptr<CWallet>& result);
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
//! -paytxfee default
constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
@@ -499,8 +501,8 @@ public:
bool IsEquivalentTo(const CWalletTx& tx) const;
bool InMempool() const;
- bool IsTrusted(interfaces::Chain::Lock& locked_chain) const;
- bool IsTrusted(interfaces::Chain::Lock& locked_chain, std::set<uint256>& trusted_parents) const;
+ bool IsTrusted() const;
+ bool IsTrusted(std::set<uint256>& trusted_parents) const;
int64_t GetTxTime() const;
@@ -775,8 +777,8 @@ public:
bool IsLocked() const override;
bool Lock();
- /** Interface to assert chain access and if successful lock it */
- std::unique_ptr<interfaces::Chain::Lock> LockChain() { return m_chain ? m_chain->lock() : nullptr; }
+ /** Interface to assert chain access */
+ bool HaveChain() const { return m_chain ? true : false; }
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
@@ -805,12 +807,12 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe = true, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe = true, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
*/
- std::map<CTxDestination, std::vector<COutput>> ListCoins(interfaces::Chain::Lock& locked_chain) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Find non-change parent output.
@@ -875,7 +877,7 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
- void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
/**
@@ -930,7 +932,7 @@ public:
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
+ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
// Fetch the inputs and sign with SIGHASH_ALL.
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Sign the tx given the input coins and sighash.
@@ -961,8 +963,7 @@ public:
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
- bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut,
- std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
+ bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign = true);
/**
* Submit the transaction to the node's mempool and then relay to peers.
* Should be called after CreateTransaction unless you want to abort
@@ -1012,7 +1013,7 @@ public:
int64_t GetOldestKeyPoolTime() const;
std::set<std::set<CTxDestination>> GetAddressGroupings() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::map<CTxDestination, CAmount> GetAddressBalances(interfaces::Chain::Lock& locked_chain) const;
+ std::map<CTxDestination, CAmount> GetAddressBalances() const;
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
@@ -1125,10 +1126,10 @@ public:
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
//! Verify wallet naming and perform salvage on the wallet if required
- static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::vector<std::string>& warnings);
+ static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector<std::string>& warnings, uint64_t wallet_creation_flags = 0);
+ static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0);
/**
* Wallet post-init setup
@@ -1181,7 +1182,7 @@ public:
};
/** Upgrade the wallet */
- bool UpgradeWallet(int version, std::string& error, std::vector<std::string>& warnings);
+ bool UpgradeWallet(int version, bilingual_str& error, std::vector<bilingual_str>& warnings);
//! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers
std::set<ScriptPubKeyMan*> GetActiveScriptPubKeyMans() const;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 79316ca3e7..45033f9bac 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -913,12 +913,12 @@ bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, C
return true;
}
-bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr)
+bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr)
{
return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);
}
-bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::vector<std::string>& warnings, std::string& errorStr)
+bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr)
{
return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warnings, errorStr, WalletBatch::Recover);
}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 2701481c58..ae72a5b265 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -272,9 +272,9 @@ public:
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
- static bool VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr);
+ static bool VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& wallet_path, std::vector<std::string>& warnings, std::string& errorStr);
+ static bool VerifyDatabaseFile(const fs::path& wallet_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 65643669c2..522efaa884 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -4,6 +4,7 @@
#include <fs.h>
#include <util/system.h>
+#include <util/translation.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -117,9 +118,9 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
return false;
}
- std::string error;
+ bilingual_str error;
if (!WalletBatch::VerifyEnvironment(path, error)) {
- tfm::format(std::cerr, "Error loading %s. Is wallet being used by other process?\n", name);
+ tfm::format(std::cerr, "%s\nError loading %s. Is wallet being used by other process?\n", error.original, name);
return false;
}
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 166c28d376..9cff79a42c 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -18,15 +18,16 @@ needs an older patch version.
import os
import shutil
-from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
from test_framework.util import (
assert_equal,
sync_blocks,
- sync_mempools
+ sync_mempools,
)
+
class BackwardsCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -42,35 +43,15 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ self.skip_if_no_previous_releases()
def setup_nodes(self):
- if os.getenv("TEST_PREVIOUS_RELEASES") == "false":
- raise SkipTest("backwards compatibility tests")
-
- releases_path = os.getenv("PREVIOUS_RELEASES_DIR") or os.getcwd() + "/releases"
- if not os.path.isdir(releases_path):
- if os.getenv("TEST_PREVIOUS_RELEASES") == "true":
- raise AssertionError("TEST_PREVIOUS_RELEASES=1 but releases missing: " + releases_path)
- raise SkipTest("This test requires binaries for previous releases")
-
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
None,
- 190000,
+ 190001,
180100,
- 170100
- ], binary=[
- self.options.bitcoind,
- self.options.bitcoind,
- releases_path + "/v0.19.0.1/bin/bitcoind",
- releases_path + "/v0.18.1/bin/bitcoind",
- releases_path + "/v0.17.1/bin/bitcoind"
- ], binary_cli=[
- self.options.bitcoincli,
- self.options.bitcoincli,
- releases_path + "/v0.19.0.1/bin/bitcoin-cli",
- releases_path + "/v0.18.1/bin/bitcoin-cli",
- releases_path + "/v0.17.1/bin/bitcoin-cli"
+ 170100,
])
self.start_nodes()
@@ -321,9 +302,9 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
node_v17.rpc.createwallet(wallet_name="u1_v17")
wallet = node_v17.get_wallet_rpc("u1_v17")
address = wallet.getnewaddress("bech32")
- info = wallet.getaddressinfo(address)
- hdkeypath = info["hdkeypath"]
- pubkey = info["pubkey"]
+ v17_info = wallet.getaddressinfo(address)
+ hdkeypath = v17_info["hdkeypath"]
+ pubkey = v17_info["pubkey"]
# Copy the 0.17 wallet to the last Bitcoin Core version and open it:
node_v17.unloadwallet("u1_v17")
@@ -337,6 +318,18 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + pubkey + ")"
assert_equal(info["desc"], descsum_create(descriptor))
+ # Now copy that same wallet back to 0.17 to make sure no automatic upgrade breaks it
+ node_master.unloadwallet("u1_v17")
+ shutil.rmtree(os.path.join(node_v17_wallets_dir, "u1_v17"))
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, "u1_v17"),
+ os.path.join(node_v17_wallets_dir, "u1_v17")
+ )
+ node_v17.loadwallet("u1_v17")
+ wallet = node_v17.get_wallet_rpc("u1_v17")
+ info = wallet.getaddressinfo(address)
+ assert_equal(info, v17_info)
+
# Copy the 0.19 wallet to the last Bitcoin Core version and open it:
shutil.copytree(
os.path.join(node_v19_wallets_dir, "w1_v19"),
@@ -346,5 +339,16 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
wallet = node_master.get_wallet_rpc("w1_v19")
assert wallet.getaddressinfo(address_18075)["solvable"]
+ # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
+ node_master.unloadwallet("w1_v19")
+ shutil.rmtree(os.path.join(node_v19_wallets_dir, "w1_v19"))
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, "w1_v19"),
+ os.path.join(node_v19_wallets_dir, "w1_v19")
+ )
+ node_v19.loadwallet("w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+
if __name__ == '__main__':
BackwardsCompatibilityTest().main()
diff --git a/test/functional/framework_test_script.py b/test/functional/framework_test_script.py
deleted file mode 100755
index 9d916c0022..0000000000
--- a/test/functional/framework_test_script.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2020 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Tests for test_framework.script."""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.script import bn2vch
-from test_framework.util import assert_equal
-
-def test_bn2vch():
- assert_equal(bn2vch(0), bytes([]))
- assert_equal(bn2vch(1), bytes([0x01]))
- assert_equal(bn2vch(-1), bytes([0x81]))
- assert_equal(bn2vch(0x7F), bytes([0x7F]))
- assert_equal(bn2vch(-0x7F), bytes([0xFF]))
- assert_equal(bn2vch(0x80), bytes([0x80, 0x00]))
- assert_equal(bn2vch(-0x80), bytes([0x80, 0x80]))
- assert_equal(bn2vch(0xFF), bytes([0xFF, 0x00]))
- assert_equal(bn2vch(-0xFF), bytes([0xFF, 0x80]))
- assert_equal(bn2vch(0x100), bytes([0x00, 0x01]))
- assert_equal(bn2vch(-0x100), bytes([0x00, 0x81]))
- assert_equal(bn2vch(0x7FFF), bytes([0xFF, 0x7F]))
- assert_equal(bn2vch(-0x8000), bytes([0x00, 0x80, 0x80]))
- assert_equal(bn2vch(-0x7FFFFF), bytes([0xFF, 0xFF, 0xFF]))
- assert_equal(bn2vch(0x80000000), bytes([0x00, 0x00, 0x00, 0x80, 0x00]))
- assert_equal(bn2vch(-0x80000000), bytes([0x00, 0x00, 0x00, 0x80, 0x80]))
- assert_equal(bn2vch(0xFFFFFFFF), bytes([0xFF, 0xFF, 0xFF, 0xFF, 0x00]))
-
- assert_equal(bn2vch(123456789), bytes([0x15, 0xCD, 0x5B, 0x07]))
- assert_equal(bn2vch(-54321), bytes([0x31, 0xD4, 0x80]))
-
-class FrameworkTestScript(BitcoinTestFramework):
- def setup_network(self):
- pass
-
- def set_test_params(self):
- self.num_nodes = 0
-
- def run_test(self):
- test_bn2vch()
-
-if __name__ == '__main__':
- FrameworkTestScript().main()
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 7f7430d04e..157af68203 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -141,12 +141,11 @@ class P2PLeakTest(BitcoinTestFramework):
assert no_verack_idlenode.unexpected_msg == False
self.log.info('Check that the version message does not leak the local address of the node')
- time_begin = int(time.time())
p2p_version_store = self.nodes[0].add_p2p_connection(P2PVersionStore())
- time_end = time.time()
ver = p2p_version_store.version_received
- assert_greater_than_or_equal(ver.nTime, time_begin)
- assert_greater_than_or_equal(time_end, ver.nTime)
+ # Check that received time is within one hour of now
+ assert_greater_than_or_equal(ver.nTime, time.time() - 3600)
+ assert_greater_than_or_equal(time.time() + 3600, ver.nTime)
assert_equal(ver.addrFrom.port, 0)
assert_equal(ver.addrFrom.ip, '0.0.0.0')
assert_equal(ver.nStartingHeight, 201)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index dbdce6552a..6fb0fec32b 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -1896,12 +1896,12 @@ class SegWitTest(BitcoinTestFramework):
def test_upgrade_after_activation(self):
"""Test the behavior of starting up a segwit-aware node after the softfork has activated."""
- # Restart with the new binary
self.stop_node(2)
self.start_node(2, extra_args=["-segwitheight={}".format(SEGWIT_HEIGHT)])
connect_nodes(self.nodes[0], 2)
- self.sync_blocks()
+ # We reconnect more than 100 blocks, give it plenty of time
+ self.sync_blocks(timeout=240)
# Make sure that this peer thinks segwit has activated.
assert softfork_active(self.nodes[2], 'segwit')
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 257499fcb9..31cec66ee7 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -120,8 +120,9 @@ class P2PConnection(asyncio.Protocol):
def is_connected(self):
return self._transport is not None
- def peer_connect(self, dstaddr, dstport, *, net):
+ def peer_connect(self, dstaddr, dstport, *, net, factor):
assert not self.is_connected
+ self.factor = factor
self.dstaddr = dstaddr
self.dstport = dstport
# The initial message to send after the connection was made:
@@ -367,9 +368,12 @@ class P2PInterface(P2PConnection):
# Connection helper methods
+ def wait_until(self, test_function, timeout):
+ wait_until(test_function, timeout=timeout, lock=mininode_lock, factor=self.factor)
+
def wait_for_disconnect(self, timeout=60):
test_function = lambda: not self.is_connected
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
# Message receiving helper methods
@@ -380,14 +384,14 @@ class P2PInterface(P2PConnection):
return False
return self.last_message['tx'].tx.rehash() == txid
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_block(self, blockhash, timeout=60):
def test_function():
assert self.is_connected
return self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_header(self, blockhash, timeout=60):
def test_function():
@@ -397,7 +401,7 @@ class P2PInterface(P2PConnection):
return False
return last_headers.headers[0].rehash() == int(blockhash, 16)
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_merkleblock(self, blockhash, timeout=60):
def test_function():
@@ -407,7 +411,7 @@ class P2PInterface(P2PConnection):
return False
return last_filtered_block.merkleblock.header.rehash() == int(blockhash, 16)
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_getdata(self, hash_list, timeout=60):
"""Waits for a getdata message.
@@ -421,7 +425,7 @@ class P2PInterface(P2PConnection):
return False
return [x.hash for x in last_data.inv] == hash_list
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_getheaders(self, timeout=60):
"""Waits for a getheaders message.
@@ -435,7 +439,7 @@ class P2PInterface(P2PConnection):
assert self.is_connected
return self.last_message.get("getheaders")
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_inv(self, expected_inv, timeout=60):
"""Waits for an INV message and checks that the first inv object in the message was as expected."""
@@ -448,13 +452,13 @@ class P2PInterface(P2PConnection):
self.last_message["inv"].inv[0].type == expected_inv[0].type and \
self.last_message["inv"].inv[0].hash == expected_inv[0].hash
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def wait_for_verack(self, timeout=60):
def test_function():
return self.message_count["verack"]
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
# Message sending helper functions
@@ -470,7 +474,7 @@ class P2PInterface(P2PConnection):
assert self.is_connected
return self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
self.ping_counter += 1
@@ -586,7 +590,7 @@ class P2PDataStore(P2PInterface):
self.send_message(msg_block(block=b))
else:
self.send_message(msg_headers([CBlockHeader(block) for block in blocks]))
- wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock)
+ self.wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout)
if expect_disconnect:
self.wait_for_disconnect(timeout=timeout)
@@ -594,7 +598,7 @@ class P2PDataStore(P2PInterface):
self.sync_with_ping(timeout=timeout)
if success:
- wait_until(lambda: node.getbestblockhash() == blocks[-1].hash, timeout=timeout)
+ self.wait_until(lambda: node.getbestblockhash() == blocks[-1].hash, timeout=timeout)
else:
assert node.getbestblockhash() != blocks[-1].hash
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index e475ed8596..9102266456 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -8,6 +8,7 @@ This file is modified from python-bitcoinlib.
"""
import hashlib
import struct
+import unittest
from .messages import (
CTransaction,
@@ -708,3 +709,25 @@ def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
ss += struct.pack("<I", hashtype)
return hash256(ss)
+
+class TestFrameworkScript(unittest.TestCase):
+ def test_bn2vch(self):
+ self.assertEqual(bn2vch(0), bytes([]))
+ self.assertEqual(bn2vch(1), bytes([0x01]))
+ self.assertEqual(bn2vch(-1), bytes([0x81]))
+ self.assertEqual(bn2vch(0x7F), bytes([0x7F]))
+ self.assertEqual(bn2vch(-0x7F), bytes([0xFF]))
+ self.assertEqual(bn2vch(0x80), bytes([0x80, 0x00]))
+ self.assertEqual(bn2vch(-0x80), bytes([0x80, 0x80]))
+ self.assertEqual(bn2vch(0xFF), bytes([0xFF, 0x00]))
+ self.assertEqual(bn2vch(-0xFF), bytes([0xFF, 0x80]))
+ self.assertEqual(bn2vch(0x100), bytes([0x00, 0x01]))
+ self.assertEqual(bn2vch(-0x100), bytes([0x00, 0x81]))
+ self.assertEqual(bn2vch(0x7FFF), bytes([0xFF, 0x7F]))
+ self.assertEqual(bn2vch(-0x8000), bytes([0x00, 0x80, 0x80]))
+ self.assertEqual(bn2vch(-0x7FFFFF), bytes([0xFF, 0xFF, 0xFF]))
+ self.assertEqual(bn2vch(0x80000000), bytes([0x00, 0x00, 0x00, 0x80, 0x00]))
+ self.assertEqual(bn2vch(-0x80000000), bytes([0x00, 0x00, 0x00, 0x80, 0x80]))
+ self.assertEqual(bn2vch(0xFFFFFFFF), bytes([0xFF, 0xFF, 0xFF, 0xFF, 0x00]))
+ self.assertEqual(bn2vch(123456789), bytes([0x15, 0xCD, 0x5B, 0x07]))
+ self.assertEqual(bn2vch(-54321), bytes([0x31, 0xD4, 0x80]))
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 8719bd0d39..11c96deefb 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -6,11 +6,12 @@
import configparser
from enum import Enum
-import logging
import argparse
+import logging
import os
import pdb
import random
+import re
import shutil
import subprocess
import sys
@@ -101,6 +102,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.bind_to_localhost_only = True
self.set_test_params()
self.parse_args()
+ self.rpc_timeout = int(self.rpc_timeout * self.options.factor) # optionally, increase timeout by a factor
def main(self):
"""Main function. This should not be overridden by the subclass test scripts."""
@@ -167,6 +169,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
help="set a random seed for deterministically reproducing a previous test run")
parser.add_argument("--descriptors", default=False, action="store_true",
help="Run test using a descriptor wallet")
+ parser.add_argument('--factor', type=float, default=1.0, help='adjust test timeouts by a factor')
self.add_options(parser)
self.options = parser.parse_args()
@@ -185,10 +188,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/bitcoind' + config["environment"]["EXEEXT"])
self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/bitcoin-cli' + config["environment"]["EXEEXT"])
+ self.options.previous_releases_path = os.getenv("PREVIOUS_RELEASES_DIR") or os.getcwd() + "/releases"
+
os.environ['PATH'] = os.pathsep.join([
os.path.join(config['environment']['BUILDDIR'], 'src'),
- os.path.join(config['environment']['BUILDDIR'], 'src', 'qt'),
- os.environ['PATH']
+ os.path.join(config['environment']['BUILDDIR'], 'src', 'qt'), os.environ['PATH']
])
# Set up temp directory and start logging
@@ -388,6 +392,25 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
Should only be called once after the nodes have been specified in
set_test_params()."""
+ def get_bin_from_version(version, bin_name, bin_default):
+ if not version:
+ return bin_default
+ return os.path.join(
+ self.options.previous_releases_path,
+ re.sub(
+ r'\.0$',
+ '', # remove trailing .0 for point releases
+ 'v{}.{}.{}.{}'.format(
+ (version % 100000000) // 1000000,
+ (version % 1000000) // 10000,
+ (version % 10000) // 100,
+ (version % 100) // 1,
+ ),
+ ),
+ 'bin',
+ bin_name,
+ )
+
if self.bind_to_localhost_only:
extra_confs = [["bind=127.0.0.1"]] * num_nodes
else:
@@ -397,9 +420,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if versions is None:
versions = [None] * num_nodes
if binary is None:
- binary = [self.options.bitcoind] * num_nodes
+ binary = [get_bin_from_version(v, 'bitcoind', self.options.bitcoind) for v in versions]
if binary_cli is None:
- binary_cli = [self.options.bitcoincli] * num_nodes
+ binary_cli = [get_bin_from_version(v, 'bitcoin-cli', self.options.bitcoincli) for v in versions]
assert_equal(len(extra_confs), num_nodes)
assert_equal(len(extra_args), num_nodes)
assert_equal(len(versions), num_nodes)
@@ -412,6 +435,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
chain=self.chain,
rpchost=rpchost,
timewait=self.rpc_timeout,
+ factor=self.options.factor,
bitcoind=binary[i],
bitcoin_cli=binary_cli[i],
version=versions[i],
@@ -558,6 +582,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args=['-disablewallet'],
rpchost=None,
timewait=self.rpc_timeout,
+ factor=self.options.factor,
bitcoind=self.options.bitcoind,
bitcoin_cli=self.options.bitcoincli,
coverage_dir=None,
@@ -640,6 +665,25 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if not self.is_cli_compiled():
raise SkipTest("bitcoin-cli has not been compiled.")
+ def skip_if_no_previous_releases(self):
+ """Skip the running test if previous releases are not available."""
+ if not self.has_previous_releases():
+ raise SkipTest("previous releases not available or disabled")
+
+ def has_previous_releases(self):
+ """Checks whether previous releases are present and enabled."""
+ if os.getenv("TEST_PREVIOUS_RELEASES") == "false":
+ # disabled
+ return False
+
+ if not os.path.isdir(self.options.previous_releases_path):
+ if os.getenv("TEST_PREVIOUS_RELEASES") == "true":
+ raise AssertionError("TEST_PREVIOUS_RELEASES=true but releases missing: {}".format(
+ self.options.previous_releases_path))
+ # missing
+ return False
+ return True
+
def is_cli_compiled(self):
"""Checks whether bitcoin-cli was compiled."""
return self.config["components"].getboolean("ENABLE_CLI")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index c0075fd8ec..e6ec3c1b2d 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -62,7 +62,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False):
+ def __init__(self, i, datadir, *, chain, rpchost, timewait, factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False):
"""
Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -128,6 +128,7 @@ class TestNode():
self.perf_subprocesses = {}
self.p2ps = []
+ self.factor = factor
AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key'])
PRIV_KEYS = [
@@ -324,13 +325,13 @@ class TestNode():
return True
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
- wait_until(self.is_node_stopped, timeout=timeout)
+ wait_until(self.is_node_stopped, timeout=timeout, factor=self.factor)
@contextlib.contextmanager
def assert_debug_log(self, expected_msgs, unexpected_msgs=None, timeout=2):
if unexpected_msgs is None:
unexpected_msgs = []
- time_end = time.time() + timeout
+ time_end = time.time() + timeout * self.factor
debug_log = os.path.join(self.datadir, self.chain, 'debug.log')
with open(debug_log, encoding='utf-8') as dl:
dl.seek(0, 2)
@@ -487,7 +488,7 @@ class TestNode():
if 'dstaddr' not in kwargs:
kwargs['dstaddr'] = '127.0.0.1'
- p2p_conn.peer_connect(**kwargs, net=self.chain)()
+ p2p_conn.peer_connect(**kwargs, net=self.chain, factor=self.factor)()
self.p2ps.append(p2p_conn)
if wait_for_verack:
# Wait for the node to send us the version and verack
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index eb9f6528b3..20ab9ee464 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -208,9 +208,10 @@ def str_to_b64str(string):
def satoshi_round(amount):
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
-def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None):
+def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, factor=1.0):
if attempts == float('inf') and timeout == float('inf'):
timeout = 60
+ timeout = timeout * factor
attempt = 0
time_end = time.time() + timeout
@@ -265,7 +266,7 @@ def get_rpc_proxy(url, node_number, *, timeout=None, coveragedir=None):
"""
proxy_kwargs = {}
if timeout is not None:
- proxy_kwargs['timeout'] = timeout
+ proxy_kwargs['timeout'] = int(timeout)
proxy = AuthServiceProxy(url, **proxy_kwargs)
proxy.url = url # store URL on proxy for info
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 6f5f61de2c..45f591ba4a 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -24,6 +24,7 @@ import sys
import tempfile
import re
import logging
+import unittest
# Formatting. Default colors to empty strings.
BOLD, GREEN, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
@@ -65,6 +66,10 @@ if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
+TEST_FRAMEWORK_MODULES = [
+ "script",
+]
+
EXTENDED_SCRIPTS = [
# These tests are not run by default.
# Longest test should go first, to favor running tests in parallel
@@ -237,7 +242,6 @@ BASE_SCRIPTS = [
'rpc_help.py',
'feature_help.py',
'feature_shutdown.py',
- 'framework_test_script.py',
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
]
@@ -400,6 +404,16 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
if os.path.isdir(cache_dir):
print("%sWARNING!%s There is a cache directory here: %s. If tests fail unexpectedly, try deleting the cache directory." % (BOLD[1], BOLD[0], cache_dir))
+ # Test Framework Tests
+ print("Running Unit Tests for Test Framework Modules")
+ test_framework_tests = unittest.TestSuite()
+ for module in TEST_FRAMEWORK_MODULES:
+ test_framework_tests.addTest(unittest.TestLoader().loadTestsFromName("test_framework.{}".format(module)))
+ result = unittest.TextTestRunner(verbosity=1, failfast=True).run(test_framework_tests)
+ if not result.wasSuccessful():
+ logging.debug("Early exiting after failure in TestFramework unit tests")
+ sys.exit(False)
+
tests_dir = src_dir + '/test/functional/'
flags = ['--cachedir={}'.format(cache_dir)] + args
@@ -623,7 +637,7 @@ class TestResult():
def check_script_prefixes():
"""Check that test scripts start with one of the allowed name prefixes."""
- good_prefixes_re = re.compile("^(example|feature|interface|mempool|mining|p2p|rpc|wallet|tool|framework_test)_")
+ good_prefixes_re = re.compile("^(example|feature|interface|mempool|mining|p2p|rpc|wallet|tool)_")
bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None]
if bad_script_names:
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index b3d496dd51..039ce7daee 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -15,6 +15,7 @@ from test_framework.util import assert_equal
BUFFER_SIZE = 16 * 1024
+
class ToolWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -48,7 +49,7 @@ class ToolWalletTest(BitcoinTestFramework):
h = hashlib.sha1()
mv = memoryview(bytearray(BUFFER_SIZE))
with open(self.wallet_path, 'rb', buffering=0) as f:
- for n in iter(lambda : f.readinto(mv), 0):
+ for n in iter(lambda: f.readinto(mv), 0):
h.update(mv[:n])
return h.hexdigest()
@@ -69,7 +70,12 @@ class ToolWalletTest(BitcoinTestFramework):
self.assert_raises_tool_error('Invalid command: help', 'help')
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
- self.assert_raises_tool_error('Error loading wallet.dat. Is wallet being used by other process?', '-wallet=wallet.dat', 'info')
+ self.assert_raises_tool_error(
+ 'Error initializing wallet database environment "{}"!\nError loading wallet.dat. Is wallet being used by other process?'
+ .format(os.path.join(self.nodes[0].datadir, self.chain, 'wallets')),
+ '-wallet=wallet.dat',
+ 'info',
+ )
self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
def test_tool_wallet_info(self):
@@ -84,7 +90,7 @@ class ToolWalletTest(BitcoinTestFramework):
#
# self.log.debug('Setting wallet file permissions to 400 (read-only)')
# os.chmod(self.wallet_path, stat.S_IRUSR)
- # assert(self.wallet_permissions() in ['400', '666']) # Sanity check. 666 because Appveyor.
+ # assert self.wallet_permissions() in ['400', '666'] # Sanity check. 666 because Appveyor.
# shasum_before = self.wallet_shasum()
timestamp_before = self.wallet_timestamp()
self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
@@ -103,7 +109,7 @@ class ToolWalletTest(BitcoinTestFramework):
self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
self.log.debug('Setting wallet file permissions back to 600 (read/write)')
os.chmod(self.wallet_path, stat.S_IRUSR | stat.S_IWUSR)
- assert(self.wallet_permissions() in ['600', '666']) # Sanity check. 666 because Appveyor.
+ assert self.wallet_permissions() in ['600', '666'] # Sanity check. 666 because Appveyor.
#
# TODO: Wallet tool info should not write to the wallet file.
# The following lines should be uncommented and the tests still succeed:
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 9ba23db42d..27197e3b6d 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -300,7 +300,7 @@ def test_maxtxfee_fails(self, rbf_node, dest_address):
self.restart_node(1, ['-maxtxfee=0.000025'] + self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
rbfid = spend_one_input(rbf_node, dest_address)
- assert_raises_rpc_error(-4, "Unable to create transaction: Fee exceeds maximum configured by -maxtxfee", rbf_node.bumpfee, rbfid)
+ assert_raises_rpc_error(-4, "Unable to create transaction. Fee exceeds maximum configured by -maxtxfee", rbf_node.bumpfee, rbfid)
self.restart_node(1, self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
@@ -517,7 +517,7 @@ def test_no_more_inputs_fails(self, rbf_node, dest_address):
rbf_node.generatetoaddress(1, dest_address)
# spend all funds, no change output
rbfid = rbf_node.sendtoaddress(rbf_node.getnewaddress(), rbf_node.getbalance(), "", "", True)
- assert_raises_rpc_error(-4, "Unable to create transaction: Insufficient funds", rbf_node.bumpfee, rbfid)
+ assert_raises_rpc_error(-4, "Unable to create transaction. Insufficient funds", rbf_node.bumpfee, rbfid)
if __name__ == "__main__":
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index c569416292..580a61f9f3 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -227,10 +227,10 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-18, 'Wallet wallets not found.', self.nodes[0].loadwallet, 'wallets')
# Fail to load duplicate wallets
- assert_raises_rpc_error(-4, 'Wallet file verification failed: Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0])
+ assert_raises_rpc_error(-4, 'Wallet file verification failed. Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0])
# Fail to load duplicate wallets by different ways (directory and filepath)
- assert_raises_rpc_error(-4, "Wallet file verification failed: Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat')
+ assert_raises_rpc_error(-4, "Wallet file verification failed. Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat')
# Fail to load if one wallet is a copy of another
assert_raises_rpc_error(-4, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
@@ -240,7 +240,7 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load if wallet file is a symlink
- assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
+ assert_raises_rpc_error(-4, "Wallet file verification failed. Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
# Fail to load if a directory is specified that doesn't contain a wallet
os.mkdir(wallet_dir('empty_wallet_dir'))
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index d04bc4ce44..e7e71bf3f6 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -12,14 +12,15 @@ contrib/devtools/previous_release.sh -b v0.15.2 v0.16.3
import os
import shutil
-from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
adjust_bitcoin_conf_for_pre_17,
assert_equal,
assert_greater_than,
- assert_is_hex_string
+ assert_is_hex_string,
)
+
class UpgradeWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -32,32 +33,16 @@ class UpgradeWalletTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ self.skip_if_no_previous_releases()
def setup_network(self):
self.setup_nodes()
def setup_nodes(self):
- if os.getenv("TEST_PREVIOUS_RELEASES") == "false":
- raise SkipTest("upgradewallet RPC tests")
-
- releases_path = os.getenv("PREVIOUS_RELEASES_DIR") or os.getcwd() + "/releases"
- if not os.path.isdir(releases_path):
- if os.getenv("TEST_PREVIOUS_RELEASES") == "true":
- raise AssertionError("TEST_PREVIOUS_RELEASES=1 but releases missing: " + releases_path)
- raise SkipTest("This test requires binaries for previous releases")
-
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
160300,
- 150200
- ], binary=[
- self.options.bitcoind,
- releases_path + "/v0.16.3/bin/bitcoind",
- releases_path + "/v0.15.2/bin/bitcoind",
- ], binary_cli=[
- self.options.bitcoincli,
- releases_path + "/v0.16.3/bin/bitcoin-cli",
- releases_path + "/v0.15.2/bin/bitcoin-cli",
+ 150200,
])
# adapt bitcoin.conf, because older bitcoind's don't recognize config sections
adjust_bitcoin_conf_for_pre_17(self.nodes[1].bitcoinconf)