From b15a94a618c53041e97ccfface3045a0642777e1 Mon Sep 17 00:00:00 2001 From: David Gumberg Date: Thu, 22 May 2025 18:49:00 -0700 Subject: [PATCH] refactor: Split out wallet argument loading This section is necessarily repetitive, makes CWallet::Create() easier to read, and splits out functionality that will be useful when wallet creation and loading are separated. Review with `-color-moved=dimmed-zebra` --- src/wallet/wallet.cpp | 272 ++++++++++++++++++++++-------------------- src/wallet/wallet.h | 2 + 2 files changed, 144 insertions(+), 130 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d9cf77314b8..d95701fde8f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2881,6 +2881,146 @@ std::unique_ptr MakeWalletDatabase(const std::string& name, cons return MakeDatabase(*wallet_path, options, status, error_string); } +bool CWallet::LoadWalletArgs(std::shared_ptr wallet, const WalletContext& context, bilingual_str& error, std::vector& warnings) +{ + interfaces::Chain* chain = context.chain; + const ArgsManager& args = *Assert(context.args); + + if (!args.GetArg("-addresstype", "").empty()) { + std::optional parsed = ParseOutputType(args.GetArg("-addresstype", "")); + if (!parsed) { + error = strprintf(_("Unknown address type '%s'"), args.GetArg("-addresstype", "")); + return false; + } + wallet->m_default_address_type = parsed.value(); + } + + if (!args.GetArg("-changetype", "").empty()) { + std::optional parsed = ParseOutputType(args.GetArg("-changetype", "")); + if (!parsed) { + error = strprintf(_("Unknown change type '%s'"), args.GetArg("-changetype", "")); + return false; + } + wallet->m_default_change_type = parsed.value(); + } + + if (const auto arg{args.GetArg("-mintxfee")}) { + std::optional min_tx_fee = ParseMoney(*arg); + if (!min_tx_fee) { + error = AmountErrMsg("mintxfee", *arg); + return false; + } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { + warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") + + _("This is the minimum transaction fee you pay on every transaction.")); + } + + wallet->m_min_fee = CFeeRate{min_tx_fee.value()}; + } + + if (const auto arg{args.GetArg("-maxapsfee")}) { + const std::string& max_aps_fee{*arg}; + if (max_aps_fee == "-1") { + wallet->m_max_aps_fee = -1; + } else if (std::optional max_fee = ParseMoney(max_aps_fee)) { + if (max_fee.value() > HIGH_APS_FEE) { + warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") + + _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.")); + } + wallet->m_max_aps_fee = max_fee.value(); + } else { + error = AmountErrMsg("maxapsfee", max_aps_fee); + return false; + } + } + + if (const auto arg{args.GetArg("-fallbackfee")}) { + std::optional fallback_fee = ParseMoney(*arg); + if (!fallback_fee) { + error = strprintf(_("Invalid amount for %s=: '%s'"), "-fallbackfee", *arg); + return false; + } else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) { + warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") + + _("This is the transaction fee you may pay when fee estimates are not available.")); + } + wallet->m_fallback_fee = CFeeRate{fallback_fee.value()}; + } + + // Disable fallback fee in case value was set to 0, enable if non-null value + wallet->m_allow_fallback_fee = wallet->m_fallback_fee.GetFeePerK() != 0; + + if (const auto arg{args.GetArg("-discardfee")}) { + std::optional discard_fee = ParseMoney(*arg); + if (!discard_fee) { + error = strprintf(_("Invalid amount for %s=: '%s'"), "-discardfee", *arg); + return false; + } else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) { + warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") + + _("This is the transaction fee you may discard if change is smaller than dust at this level")); + } + wallet->m_discard_rate = CFeeRate{discard_fee.value()}; + } + + if (const auto arg{args.GetArg("-paytxfee")}) { + warnings.push_back(_("-paytxfee is deprecated and will be fully removed in v31.0.")); + + std::optional pay_tx_fee = ParseMoney(*arg); + if (!pay_tx_fee) { + error = AmountErrMsg("paytxfee", *arg); + return false; + } else if (pay_tx_fee.value() > HIGH_TX_FEE_PER_KB) { + warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") + + _("This is the transaction fee you will pay if you send a transaction.")); + } + + wallet->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000}; + + if (chain && wallet->m_pay_tx_fee < chain->relayMinFee()) { + error = strprintf(_("Invalid amount for %s=: '%s' (must be at least %s)"), + "-paytxfee", *arg, chain->relayMinFee().ToString()); + return false; + } + } + + if (const auto arg{args.GetArg("-maxtxfee")}) { + std::optional max_fee = ParseMoney(*arg); + if (!max_fee) { + error = AmountErrMsg("maxtxfee", *arg); + return false; + } else if (max_fee.value() > HIGH_MAX_TX_FEE) { + warnings.push_back(strprintf(_("%s is set very high! Fees this large could be paid on a single transaction."), "-maxtxfee")); + } + + if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) { + error = strprintf(_("Invalid amount for %s=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + "-maxtxfee", *arg, chain->relayMinFee().ToString()); + return false; + } + + wallet->m_default_max_tx_fee = max_fee.value(); + } + + if (const auto arg{args.GetArg("-consolidatefeerate")}) { + if (std::optional consolidate_feerate = ParseMoney(*arg)) { + wallet->m_consolidate_feerate = CFeeRate(*consolidate_feerate); + } else { + error = AmountErrMsg("consolidatefeerate", *arg); + return false; + } + } + + if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { + warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") + + _("The wallet will avoid paying less than the minimum relay fee.")); + } + + wallet->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + wallet->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + wallet->m_signal_rbf = args.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + wallet->SetBroadcastTransactions(args.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); + + return true; +} + std::shared_ptr CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector& warnings) { interfaces::Chain* chain = context.chain; @@ -2939,138 +3079,10 @@ std::shared_ptr CWallet::Create(WalletContext& context, const std::stri } } - if (!args.GetArg("-addresstype", "").empty()) { - std::optional parsed = ParseOutputType(args.GetArg("-addresstype", "")); - if (!parsed) { - error = strprintf(_("Unknown address type '%s'"), args.GetArg("-addresstype", "")); - return nullptr; - } - walletInstance->m_default_address_type = parsed.value(); + if (!LoadWalletArgs(walletInstance, context, error, warnings)) { + return nullptr; } - if (!args.GetArg("-changetype", "").empty()) { - std::optional parsed = ParseOutputType(args.GetArg("-changetype", "")); - if (!parsed) { - error = strprintf(_("Unknown change type '%s'"), args.GetArg("-changetype", "")); - return nullptr; - } - walletInstance->m_default_change_type = parsed.value(); - } - - if (const auto arg{args.GetArg("-mintxfee")}) { - std::optional min_tx_fee = ParseMoney(*arg); - if (!min_tx_fee) { - error = AmountErrMsg("mintxfee", *arg); - return nullptr; - } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { - warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") + - _("This is the minimum transaction fee you pay on every transaction.")); - } - - walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()}; - } - - if (const auto arg{args.GetArg("-maxapsfee")}) { - const std::string& max_aps_fee{*arg}; - if (max_aps_fee == "-1") { - walletInstance->m_max_aps_fee = -1; - } else if (std::optional max_fee = ParseMoney(max_aps_fee)) { - if (max_fee.value() > HIGH_APS_FEE) { - warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") + - _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.")); - } - walletInstance->m_max_aps_fee = max_fee.value(); - } else { - error = AmountErrMsg("maxapsfee", max_aps_fee); - return nullptr; - } - } - - if (const auto arg{args.GetArg("-fallbackfee")}) { - std::optional fallback_fee = ParseMoney(*arg); - if (!fallback_fee) { - error = strprintf(_("Invalid amount for %s=: '%s'"), "-fallbackfee", *arg); - return nullptr; - } else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) { - 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{fallback_fee.value()}; - } - - // Disable fallback fee in case value was set to 0, enable if non-null value - walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0; - - if (const auto arg{args.GetArg("-discardfee")}) { - std::optional discard_fee = ParseMoney(*arg); - if (!discard_fee) { - error = strprintf(_("Invalid amount for %s=: '%s'"), "-discardfee", *arg); - return nullptr; - } else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) { - 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{discard_fee.value()}; - } - - if (const auto arg{args.GetArg("-paytxfee")}) { - warnings.push_back(_("-paytxfee is deprecated and will be fully removed in v31.0.")); - - std::optional pay_tx_fee = ParseMoney(*arg); - if (!pay_tx_fee) { - error = AmountErrMsg("paytxfee", *arg); - return nullptr; - } else if (pay_tx_fee.value() > HIGH_TX_FEE_PER_KB) { - 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{pay_tx_fee.value(), 1000}; - - if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) { - error = strprintf(_("Invalid amount for %s=: '%s' (must be at least %s)"), - "-paytxfee", *arg, chain->relayMinFee().ToString()); - return nullptr; - } - } - - if (const auto arg{args.GetArg("-maxtxfee")}) { - std::optional max_fee = ParseMoney(*arg); - if (!max_fee) { - error = AmountErrMsg("maxtxfee", *arg); - return nullptr; - } else if (max_fee.value() > HIGH_MAX_TX_FEE) { - warnings.push_back(strprintf(_("%s is set very high! Fees this large could be paid on a single transaction."), "-maxtxfee")); - } - - if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) { - error = strprintf(_("Invalid amount for %s=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - "-maxtxfee", *arg, chain->relayMinFee().ToString()); - return nullptr; - } - - walletInstance->m_default_max_tx_fee = max_fee.value(); - } - - if (const auto arg{args.GetArg("-consolidatefeerate")}) { - if (std::optional consolidate_feerate = ParseMoney(*arg)) { - walletInstance->m_consolidate_feerate = CFeeRate(*consolidate_feerate); - } else { - error = AmountErrMsg("consolidatefeerate", *arg); - return nullptr; - } - } - - if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { - warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") + - _("The wallet will avoid paying less than the minimum relay fee.")); - } - - walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - walletInstance->m_signal_rbf = args.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); - walletInstance->SetBroadcastTransactions(args.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); - walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", Ticks(SteadyClock::now() - start)); // Try to top up keypool. No-op if the wallet is locked. diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1351ee18810..27ce8957ff2 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -871,6 +871,8 @@ public: /** Mark a transaction as replaced by another transaction. */ bool MarkReplaced(const Txid& originalHash, const Txid& newHash); + static bool LoadWalletArgs(std::shared_ptr wallet, const WalletContext& context, bilingual_str& error, std::vector& warnings); + /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ static std::shared_ptr Create(WalletContext& context, const std::string& name, std::unique_ptr database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector& warnings);