From fae51a5c6f4270a1088e6295b10a8cc45988ae46 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 19 Aug 2019 18:12:35 -0400 Subject: [PATCH 1/4] wallet: Avoid translating RPC errors when loading wallets Common errors and warnings should be translated when displayed in the GUI, but not translated when displayed elsewhere. The wallet method CreateWalletFromFile does not know its caller, so this commit changes it to return a bilingual_str to the caller. --- src/dummywallet.cpp | 5 +- src/interfaces/node.cpp | 8 +-- src/interfaces/node.h | 7 +- src/qt/walletcontroller.cpp | 13 ++-- src/qt/walletcontroller.h | 5 +- src/util/translation.h | 14 ++++ src/wallet/db.cpp | 10 +-- src/wallet/db.h | 6 +- src/wallet/load.cpp | 20 +++--- src/wallet/rpcwallet.cpp | 31 +++++---- src/wallet/scriptpubkeyman.cpp | 5 +- src/wallet/scriptpubkeyman.h | 5 +- src/wallet/test/wallet_tests.cpp | 5 +- src/wallet/wallet.cpp | 113 ++++++++++++++++--------------- src/wallet/wallet.h | 12 ++-- src/wallet/walletdb.cpp | 4 +- src/wallet/walletdb.h | 4 +- src/wallet/wallettool.cpp | 3 +- 18 files changed, 149 insertions(+), 121 deletions(-) diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp index a5582e3b2c8..0f7848bae18 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> GetWallets() throw std::logic_error("Wallet function called in non-wallet build."); } -std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::vector& warnings) +std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector& 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& warnings, std::shared_ptr& result) +WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::shared_ptr& result) { throw std::logic_error("Wallet function called in non-wallet build."); } diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 5ebbd615844..8564819e6ab 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -43,8 +43,8 @@ class CWallet; fs::path GetWalletDir(); std::vector ListWalletDir(); std::vector> GetWallets(); -std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::vector& warnings); -WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector& warnings, std::shared_ptr& result); +std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector& warnings); +WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::shared_ptr& result); std::unique_ptr HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet); namespace interfaces { @@ -259,11 +259,11 @@ public: } return wallets; } - std::unique_ptr loadWallet(const std::string& name, std::string& error, std::vector& warnings) override + std::unique_ptr loadWallet(const std::string& name, bilingual_str& error, std::vector& warnings) override { return MakeWallet(LoadWallet(*m_context.chain, name, error, warnings)); } - std::unique_ptr createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector& warnings, WalletCreationStatus& status) override + std::unique_ptr createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, WalletCreationStatus& status) override { std::shared_ptr 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 53a20886cd1..db9b42b2934 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 loadWallet(const std::string& name, std::string& error, std::vector& warnings) = 0; + virtual std::unique_ptr loadWallet(const std::string& name, bilingual_str& error, std::vector& warnings) = 0; //! Create a wallet from file - virtual std::unique_ptr createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector& warnings, WalletCreationStatus& status) = 0; + virtual std::unique_ptr createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, WalletCreationStatus& status) = 0; //! Register handler for init messages. using InitMessageFn = std::function; diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 9c1488fb0e4..7cde3ca30b3 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -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 3808b7d8bf8..24dd83adf74 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -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 m_warning_message; + bilingual_str m_error_message; + std::vector m_warning_message; }; diff --git a/src/util/translation.h b/src/util/translation.h index fc45da440a5..b2d964c977c 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 bilingual_str format(const bilingual_str& fmt, const Args&... args) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 67be4d85d2c..3f7c2d09cc1 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 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& warnings, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc) +bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc) { std::string walletFile; std::shared_ptr env = GetWalletEnv(file_path, walletFile); @@ -425,12 +425,12 @@ bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector +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& warnings, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc); + static bool VerifyDatabaseFile(const fs::path& file_path, std::vector& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc); template bool Read(const K& key, T& value) diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 0afd2dfcf02..217a9504574 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -53,12 +53,14 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector& wal return false; } - std::string error_string; - std::vector warnings; + bilingual_str error_string; + std::vector 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& walle { try { for (const std::string& walletFile : wallet_files) { - std::string error; - std::vector warnings; + bilingual_str error; + std::vector warnings; std::shared_ptr 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/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 91162d575dc..8343ae3360c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -2589,14 +2590,14 @@ static UniValue loadwallet(const JSONRPCRequest& request) } } - std::string error; - std::vector warning; - std::shared_ptr const wallet = LoadWallet(*g_rpc_chain, location, error, warning); - if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error); + bilingual_str error; + std::vector warnings; + std::shared_ptr 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; } @@ -2705,12 +2706,12 @@ static UniValue createwallet(const JSONRPCRequest& request) } SecureString passphrase; passphrase.reserve(100); - std::vector warnings; + std::vector 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.")); } } @@ -2721,14 +2722,14 @@ static UniValue createwallet(const JSONRPCRequest& request) flags |= WALLET_FLAG_DESCRIPTORS; } - std::string error; + bilingual_str error; std::shared_ptr 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 @@ -2736,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; } @@ -4239,12 +4240,12 @@ static UniValue upgradewallet(const JSONRPCRequest& request) version = request.params[0].get_int(); } - std::string error; - std::vector warnings; + bilingual_str error; + std::vector 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 ecb95d599d5..e4be5045e11 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 3117b13d357..5414fc59236 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -19,6 +19,7 @@ #include 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 c049f2f15f2..66e2ae9b62b 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -30,8 +31,8 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) static std::shared_ptr TestLoadWallet(interfaces::Chain& chain) { - std::string error; - std::vector warnings; + bilingual_str error; + std::vector warnings; auto wallet = CWallet::CreateWalletFromFile(chain, WalletLocation(""), error, warnings); wallet->postInitProcess(); return wallet; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a20ede59fd4..e8f9864e7cb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -149,34 +150,34 @@ void UnloadWallet(std::shared_ptr&& wallet) } } -std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector& warnings) +std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector& warnings) { try { if (!CWallet::Verify(chain, location, false, error, warnings)) { - error = "Wallet file verification failed: " + error; + error = Untranslated("Wallet file verification failed: ") + error; return nullptr; } std::shared_ptr wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings); if (!wallet) { - error = "Wallet loading failed: " + error; + error = Untranslated("Wallet loading failed: ") + 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 LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::vector& warnings) +std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector& 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& warnings, std::shared_ptr& result) +WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::shared_ptr& 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 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; } } @@ -3656,7 +3657,7 @@ std::vector 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& warnings) +bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector& warnings) { // Do some checking on wallet path. It should be either a: // @@ -3670,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; } @@ -3692,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; } @@ -3708,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::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector& warnings, uint64_t wallet_creation_flags) +std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector& warnings, uint64_t wallet_creation_flags) { const std::string walletFile = WalletDataFilePath(location.GetPath()).string(); @@ -3721,7 +3722,7 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, std::unique_ptr tempWallet = MakeUnique(&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; } } @@ -3736,26 +3737,26 @@ std::shared_ptr 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; } } @@ -3781,7 +3782,7 @@ std::shared_ptr 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; } } @@ -3791,36 +3792,36 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, 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); } @@ -3828,12 +3829,12 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, if (gArgs.IsArgSet("-fallbackfee")) { CAmount nFeePerK = 0; if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { - error = strprintf(_("Invalid amount for -fallbackfee=: '%s'").translated, gArgs.GetArg("-fallbackfee", "")); + error = strprintf(_("Invalid amount for -fallbackfee=: '%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); } @@ -3843,28 +3844,28 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, if (gArgs.IsArgSet("-discardfee")) { CAmount nFeePerK = 0; if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) { - error = strprintf(_("Invalid amount for -discardfee=: '%s'").translated, gArgs.GetArg("-discardfee", "")); + error = strprintf(_("Invalid amount for -discardfee=: '%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=: '%s' (must be at least %s)").translated, + error = strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString()); return nullptr; } @@ -3873,23 +3874,23 @@ std::shared_ptr 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=: '%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=: '%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); @@ -3949,7 +3950,7 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, } 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; } } @@ -3974,7 +3975,7 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, { WalletRescanReserver reserver(*walletInstance); 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").translated; + error = _("Failed to rescan the wallet during initialization"); return nullptr; } } @@ -4034,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& warnings) +bool CWallet::UpgradeWallet(int version, bilingual_str& error, std::vector& warnings) { int prev_version = GetVersion(); int nMaxVersion = version; @@ -4043,12 +4044,12 @@ bool CWallet::UpgradeWallet(int version, std::string& error, std::vector= 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; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 72b3cf1fb85..bd3ba562abc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -40,6 +40,8 @@ using LoadWalletFn = std::function 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& wallet); bool HasWallets(); std::vector> GetWallets(); std::shared_ptr GetWallet(const std::string& name); -std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector& warnings); +std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector& warnings); std::unique_ptr 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& warnings, std::shared_ptr& result); +WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::shared_ptr& result); //! -paytxfee default constexpr CAmount DEFAULT_PAY_TX_FEE = 0; @@ -1124,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& warnings); + static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector& warnings); /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ - static std::shared_ptr CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::vector& warnings, uint64_t wallet_creation_flags = 0); + static std::shared_ptr CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector& warnings, uint64_t wallet_creation_flags = 0); /** * Wallet post-init setup @@ -1180,7 +1182,7 @@ public: }; /** Upgrade the wallet */ - bool UpgradeWallet(int version, std::string& error, std::vector& warnings); + bool UpgradeWallet(int version, bilingual_str& error, std::vector& warnings); //! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers std::set GetActiveScriptPubKeyMans() const; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 79316ca3e75..45033f9bacf 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& warnings, std::string& errorStr) +bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::vector& 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 2701481c589..ae72a5b2653 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& warnings, std::string& errorStr); + static bool VerifyDatabaseFile(const fs::path& wallet_path, std::vector& 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 65643669c25..2193edcfa5f 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -117,7 +118,7 @@ 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); return false; From fae7776690c37104d2d4949429c5f84e6a33c576 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 18 Apr 2020 15:18:17 -0400 Subject: [PATCH 2/4] wallet: Avoid translating RPC errors when creating txs Also, mark feebumper bilingual_str as Untranslated They are technical and have previously not been translated either. It is questionable whether they can even appear in the GUI. --- src/interfaces/wallet.cpp | 6 ++-- src/interfaces/wallet.h | 7 +++-- src/qt/walletmodel.cpp | 19 ++++++------ src/wallet/feebumper.cpp | 52 +++++++++++++++++--------------- src/wallet/feebumper.h | 21 +++++++------ src/wallet/rpcwallet.cpp | 38 +++++++++++------------ src/wallet/test/wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 36 +++++++++++----------- src/wallet/wallet.h | 4 +-- 9 files changed, 95 insertions(+), 90 deletions(-) diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 752448aac79..13b034936bf 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -219,7 +219,7 @@ public: bool sign, int& change_pos, CAmount& fee, - std::string& fail_reason) override + bilingual_str& fail_reason) override { LOCK(m_wallet->cs_wallet); CTransactionRef tx; @@ -248,7 +248,7 @@ public: } bool createBumpTransaction(const uint256& txid, const CCoinControl& coin_control, - std::vector& errors, + std::vector& errors, CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx) override @@ -258,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& errors, + std::vector& errors, uint256& bumped_txid) override { return feebumper::CommitTransaction(*m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) == diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index e5e3b806638..f35335c69fb 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& errors, + std::vector& 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& errors, + std::vector& errors, uint256& bumped_txid) = 0; //! Get a transaction. diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index e7581cd64e2..70ee7f49178 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -24,6 +24,7 @@ #include #include #include // for GetBoolArg +#include #include #include // 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 errors; + std::vector 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") + "
(" + - (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") + "
(" + - QString::fromStdString(errors[0])+")"); - return false; + QString::fromStdString(errors[0].translated)+")"); + return false; } return true; } diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 01fcb1680e7..ab34a41c7f1 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 -#include -#include -#include -#include #include #include #include #include #include +#include +#include +#include +#include +#include //! 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& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) +static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector& 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& errors) { +static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector& 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; } @@ -144,12 +146,12 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid) const CWalletTx* wtx = wallet.GetWalletTx(txid); if (wtx == nullptr) return false; - std::vector errors_dummy; + std::vector 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& errors, +Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector& errors, CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx) { // We are going to modify coin control later, copy to re-use @@ -159,7 +161,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo 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; @@ -216,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; + 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("Unable to create transaction: " + fail_reason); + errors.push_back(Untranslated("Unable to create transaction: ") + Untranslated(" ") + fail_reason); return Result::WALLET_ERROR; } @@ -242,7 +244,7 @@ bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) { return wallet.SignTransaction(mtx); } -Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector& errors, uint256& bumped_txid) +Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector& errors, uint256& bumped_txid) { LOCK(wallet.cs_wallet); if (!errors.empty()) { @@ -250,7 +252,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti } 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; @@ -275,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 fc038ae731a..50577c9d3e5 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& errors, - CAmount& old_fee, - CAmount& new_fee, - CMutableTransaction& mtx); + const uint256& txid, + const CCoinControl& coin_control, + std::vector& 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& errors, - uint256& bumped_txid); + const uint256& txid, + CMutableTransaction&& mtx, + std::vector& errors, + uint256& bumped_txid); } // namespace feebumper diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8343ae3360c..e666d55e116 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -339,16 +339,16 @@ static CTransactionRef SendMoney(CWallet* const pwallet, const CTxDestination& a // Create and send the transaction CAmount nFeeRequired = 0; - std::string strError; + bilingual_str error; std::vector vecSend; int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); CTransactionRef tx; - if (!pwallet->CreateTransaction(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; @@ -904,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(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(); } @@ -3109,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); } } @@ -3418,7 +3418,7 @@ static UniValue bumpfee(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - std::vector errors; + std::vector errors; CAmount old_fee; CAmount new_fee; CMutableTransaction mtx; @@ -3428,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; } } @@ -3456,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()); @@ -3474,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); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 66e2ae9b62b..0826b88f0a0 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -516,7 +516,7 @@ public: CTransactionRef tx; CAmount fee; int changePos = -1; - std::string error; + bilingual_str error; CCoinControl dummy; { BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e8f9864e7cb..c1abba7878e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2521,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& setSubtractFeeFromOutputs, CCoinControl coinControl) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, CCoinControl coinControl) { std::vector vecSend; @@ -2543,7 +2543,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC LOCK(cs_wallet); CTransactionRef tx_new; - if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { + if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, false)) { return false; } @@ -2659,7 +2659,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec return m_default_address_type; } -bool CWallet::CreateTransaction(const std::vector& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) +bool CWallet::CreateTransaction(const std::vector& 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); @@ -2670,7 +2670,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac { 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; @@ -2680,7 +2680,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac } if (vecSend.empty()) { - strFailReason = _("Transaction must have at least one recipient").translated; + error = _("Transaction must have at least one recipient"); return false; } @@ -2717,7 +2717,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac // 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()); @@ -2779,12 +2779,12 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac 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); @@ -2812,7 +2812,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac continue; } else { - strFailReason = _("Insufficient funds").translated; + error = _("Insufficient funds"); return false; } } @@ -2843,7 +2843,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac } else if ((unsigned int)nChangePosInOut > txNew.vout.size()) { - strFailReason = _("Change index out of range").translated; + error = _("Change index out of range"); return false; } @@ -2862,14 +2862,14 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac 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; } @@ -2909,7 +2909,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac // 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; } @@ -2962,7 +2962,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac } if (sign && !SignTransaction(txNew)) { - strFailReason = _("Signing transaction failed").translated; + error = _("Signing transaction failed"); return false; } @@ -2972,20 +2972,20 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CTransac // 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; } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index bd3ba562abc..3be34355963 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -932,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& setSubtractFeeFromOutputs, CCoinControl); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set& 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. @@ -963,7 +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(const std::vector& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); + bool CreateTransaction(const std::vector& 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 From fa59cc1c977cce8f1f28374ac2169970ca78a35f Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 18 Apr 2020 18:31:01 -0400 Subject: [PATCH 3/4] wallet: Report full error message in wallettool --- src/wallet/wallettool.cpp | 2 +- test/functional/tool_wallet.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 2193edcfa5f..522efaa8844 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -120,7 +120,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) } 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 wallet_instance = LoadWallet(name, path); diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py index b3d496dd51e..039ce7daee2 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: From fa2cce4391b0b1bda325f695bb45f7b565c8e8ea Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 21 Apr 2020 07:53:24 -0400 Subject: [PATCH 4/4] wallet: Remove trailing whitespace from potential translation strings If the potential translation strings are translated in the future, trailing whitespace is going to make translation effort harder. --- src/wallet/feebumper.cpp | 2 +- src/wallet/wallet.cpp | 4 ++-- test/functional/wallet_bumpfee.py | 4 ++-- test/functional/wallet_multiwallet.py | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index ab34a41c7f1..cacf3068911 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -220,7 +220,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo int change_pos_in_out = -1; // No requested location for change 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); + errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason); return Result::WALLET_ERROR; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c1abba7878e..a893548971d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -154,13 +154,13 @@ std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocati { try { if (!CWallet::Verify(chain, location, false, error, warnings)) { - error = Untranslated("Wallet file verification failed: ") + error; + error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error; return nullptr; } std::shared_ptr wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings); if (!wallet) { - error = Untranslated("Wallet loading failed: ") + error; + error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error; return nullptr; } AddWallet(wallet); diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 9ba23db42d0..27197e3b6d0 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 c569416292e..580a61f9f3a 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'))