refactor: Split out wallet argument loading
What changed, and why it matters
This is a code cleanup change that moves wallet command-line argument parsing into a separate helper function. It does not change what arguments are accepted, how they are validated, or any wallet behavior. There is no security issue visible in the change.
No security action needed; this is a routine refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors CWallet::Create() by extracting argument-loading logic into a new static method CWallet::LoadWalletArgs(). The logic, validation, error messages, and side effects (setting wallet fields) are preserved verbatim; only the control flow and indentation change. The function signature changes ArgsManager& to const ArgsManager& inside the helper, but the helper only reads arguments. No new attack surface, no weakened checks, no bug fixes.
Changed components
src/wallet/wallet.cppsrc/wallet/wallet.hInspect captured patch +96 / −82
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d9cf7731..d95701fd 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2881,108 +2881,55 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
return MakeDatabase(*wallet_path, options, status, error_string);
}
-std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
+bool CWallet::LoadWalletArgs(std::shared_ptr<CWallet> wallet, const WalletContext& context, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
interfaces::Chain* chain = context.chain;
- ArgsManager& args = *Assert(context.args);
- const std::string& walletFile = database->Filename();
-
- const auto start{SteadyClock::now()};
- // TODO: Can't use std::make_shared because we need a custom deleter but
- // should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), FlushAndDeleteWallet);
- walletInstance->m_keypool_size = std::max(args.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{1});
- walletInstance->m_notify_tx_changed_script = args.GetArg("-walletnotify", "");
-
- // Load wallet
- auto nLoadWalletRet = walletInstance->PopulateWalletFromDB(error, warnings);
- bool rescan_required = nLoadWalletRet == DBErrors::NEED_RESCAN;
- if (nLoadWalletRet != DBErrors::LOAD_OK && nLoadWalletRet != DBErrors::NONCRITICAL_ERROR && !rescan_required) {
- return nullptr;
- }
-
- // This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys
- const bool fFirstRun = walletInstance->m_spk_managers.empty() &&
- !walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
- !walletInstance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
- if (fFirstRun)
- {
- LOCK(walletInstance->cs_wallet);
-
- // Init with passed flags.
- // Always set the cache upgrade flag as this feature is supported from the beginning.
- walletInstance->InitWalletFlags(wallet_creation_flags | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);
-
- // Only descriptor wallets can be created
- assert(walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
-
- if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
- walletInstance->SetupDescriptorScriptPubKeyMans();
- }
-
- if (chain) {
- std::optional<int> tip_height = chain->getHeight();
- if (tip_height) {
- walletInstance->SetLastBlockProcessed(*tip_height, chain->getBlockHash(*tip_height));
- }
- }
- } 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"), walletFile);
- return nullptr;
- } 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"), walletFile));
- break;
- }
- }
- }
+ const ArgsManager& args = *Assert(context.args);
if (!args.GetArg("-addresstype", "").empty()) {
std::optional<OutputType> parsed = ParseOutputType(args.GetArg("-addresstype", ""));
if (!parsed) {
error = strprintf(_("Unknown address type '%s'"), args.GetArg("-addresstype", ""));
- return nullptr;
+ return false;
}
- walletInstance->m_default_address_type = parsed.value();
+ wallet->m_default_address_type = parsed.value();
}
if (!args.GetArg("-changetype", "").empty()) {
std::optional<OutputType> parsed = ParseOutputType(args.GetArg("-changetype", ""));
if (!parsed) {
error = strprintf(_("Unknown change type '%s'"), args.GetArg("-changetype", ""));
- return nullptr;
+ return false;
}
- walletInstance->m_default_change_type = parsed.value();
+ wallet->m_default_change_type = parsed.value();
}
if (const auto arg{args.GetArg("-mintxfee")}) {
std::optional<CAmount> min_tx_fee = ParseMoney(*arg);
if (!min_tx_fee) {
error = AmountErrMsg("mintxfee", *arg);
- return nullptr;
+ 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."));
}
- walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()};
+ 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") {
- walletInstance->m_max_aps_fee = -1;
+ wallet->m_max_aps_fee = -1;
} else if (std::optional<CAmount> 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();
+ wallet->m_max_aps_fee = max_fee.value();
} else {
error = AmountErrMsg("maxapsfee", max_aps_fee);
- return nullptr;
+ return false;
}
}
@@ -2990,27 +2937,27 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
std::optional<CAmount> fallback_fee = ParseMoney(*arg);
if (!fallback_fee) {
error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-fallbackfee", *arg);
- return nullptr;
+ 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."));
}
- walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()};
+ wallet->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;
+ wallet->m_allow_fallback_fee = wallet->m_fallback_fee.GetFeePerK() != 0;
if (const auto arg{args.GetArg("-discardfee")}) {
std::optional<CAmount> discard_fee = ParseMoney(*arg);
if (!discard_fee) {
error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-discardfee", *arg);
- return nullptr;
+ 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"));
}
- walletInstance->m_discard_rate = CFeeRate{discard_fee.value()};
+ wallet->m_discard_rate = CFeeRate{discard_fee.value()};
}
if (const auto arg{args.GetArg("-paytxfee")}) {
@@ -3019,18 +2966,18 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
std::optional<CAmount> pay_tx_fee = ParseMoney(*arg);
if (!pay_tx_fee) {
error = AmountErrMsg("paytxfee", *arg);
- return nullptr;
+ 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."));
}
- walletInstance->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000};
+ wallet->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000};
- if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) {
+ if (chain && wallet->m_pay_tx_fee < chain->relayMinFee()) {
error = strprintf(_("Invalid amount for %s=<amount>: '%s' (must be at least %s)"),
"-paytxfee", *arg, chain->relayMinFee().ToString());
- return nullptr;
+ return false;
}
}
@@ -3038,7 +2985,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
std::optional<CAmount> max_fee = ParseMoney(*arg);
if (!max_fee) {
error = AmountErrMsg("maxtxfee", *arg);
- return nullptr;
+ 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"));
}
@@ -3046,18 +2993,18 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) {
error = strprintf(_("Invalid amount for %s=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
"-maxtxfee", *arg, chain->relayMinFee().ToString());
- return nullptr;
+ return false;
}
- walletInstance->m_default_max_tx_fee = max_fee.value();
+ wallet->m_default_max_tx_fee = max_fee.value();
}
if (const auto arg{args.GetArg("-consolidatefeerate")}) {
if (std::optional<CAmount> consolidate_feerate = ParseMoney(*arg)) {
- walletInstance->m_consolidate_feerate = CFeeRate(*consolidate_feerate);
+ wallet->m_consolidate_feerate = CFeeRate(*consolidate_feerate);
} else {
error = AmountErrMsg("consolidatefeerate", *arg);
- return nullptr;
+ return false;
}
}
@@ -3066,10 +3013,75 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
_("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));
+ 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> CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
+{
+ interfaces::Chain* chain = context.chain;
+ ArgsManager& args = *Assert(context.args);
+ const std::string& walletFile = database->Filename();
+
+ const auto start{SteadyClock::now()};
+ // TODO: Can't use std::make_shared because we need a custom deleter but
+ // should be possible to use std::allocate_shared.
+ std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), FlushAndDeleteWallet);
+ walletInstance->m_keypool_size = std::max(args.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{1});
+ walletInstance->m_notify_tx_changed_script = args.GetArg("-walletnotify", "");
+
+ // Load wallet
+ auto nLoadWalletRet = walletInstance->PopulateWalletFromDB(error, warnings);
+ bool rescan_required = nLoadWalletRet == DBErrors::NEED_RESCAN;
+ if (nLoadWalletRet != DBErrors::LOAD_OK && nLoadWalletRet != DBErrors::NONCRITICAL_ERROR && !rescan_required) {
+ return nullptr;
+ }
+
+ // This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys
+ const bool fFirstRun = walletInstance->m_spk_managers.empty() &&
+ !walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
+ !walletInstance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
+ if (fFirstRun)
+ {
+ LOCK(walletInstance->cs_wallet);
+
+ // Init with passed flags.
+ // Always set the cache upgrade flag as this feature is supported from the beginning.
+ walletInstance->InitWalletFlags(wallet_creation_flags | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);
+
+ // Only descriptor wallets can be created
+ assert(walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
+
+ if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
+ walletInstance->SetupDescriptorScriptPubKeyMans();
+ }
+
+ if (chain) {
+ std::optional<int> tip_height = chain->getHeight();
+ if (tip_height) {
+ walletInstance->SetLastBlockProcessed(*tip_height, chain->getBlockHash(*tip_height));
+ }
+ }
+ } 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"), walletFile);
+ return nullptr;
+ } 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"), walletFile));
+ break;
+ }
+ }
+ }
+
+ if (!LoadWalletArgs(walletInstance, context, error, warnings)) {
+ return nullptr;
+ }
walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 1351ee18..27ce8957 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<CWallet> wallet, const WalletContext& context, bilingual_str& error, 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> Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.