refactor: wallet: Don't reuse WALLET_BLANK flag for born-encrypted wallets.
What changed, and why it matters
This is a code cleanup (refactor) in Bitcoin Core's wallet creation logic. Previously, the software temporarily marked encrypted wallets as 'blank' to prevent keys from being generated before encryption, then removed that flag afterward. The change makes the code more explicit by passing a separate 'born_encrypted' parameter instead of reusing the blank-wallet flag. There is no security vulnerability visible in the diff.
No security action required. Treat as normal code-quality review.
Security signals we found
No memory-safety issues, input validation bypasses, or cryptographic changes
No change to encryption algorithm or passphrase handling
Refactor only: equivalent functional behavior achieved through a dedicated parameter
No new external interfaces, RPC changes, or file-format changes
Evidence from the diff
The commit refactors CWallet::CreateNew to accept a new boolean born_encrypted parameter. It removes the previous pattern of OR-ing WALLET_FLAG_BLANK_WALLET into wallet_creation_flags when a passphrase is present, and later calling UnsetWalletFlag if the user did not actually request a blank wallet. Instead, CreateNew now skips SetupWalletGeneration() when born_encrypted is true, and the caller still runs EncryptWallet afterward. Callers in migration and test code pass false. The behavior for encrypted wallets remains the same: no descriptors generated until after encryption.
Changed components
src/wallet/wallet.cppsrc/wallet/wallet.hsrc/wallet/test/util.cppInspect captured patch +13 / −21
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
index 9ee63f6e..d2608fcc 100644
--- a/src/wallet/test/util.cpp
+++ b/src/wallet/test/util.cpp
@@ -51,7 +51,7 @@ std::shared_ptr<CWallet> TestCreateWallet(std::unique_ptr<WalletDatabase> databa
{
bilingual_str _error;
std::vector<bilingual_str> _warnings;
- auto wallet = CWallet::CreateNew(context, "", std::move(database), create_flags, _error, _warnings);
+ auto wallet = CWallet::CreateNew(context, "", std::move(database), create_flags, /*born_encrypted=*/false, _error, _warnings);
NotifyWalletLoaded(context, wallet);
if (context.chain) {
wallet->postInitProcess();
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index ade5b48f..c337c45d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -385,18 +385,12 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
uint64_t wallet_creation_flags = options.create_flags;
const SecureString& passphrase = options.create_passphrase;
+ bool born_encrypted = !passphrase.empty();
// Only descriptor wallets can be created
Assert(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS);
options.require_format = DatabaseFormat::SQLITE;
- // 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);
-
- // Born encrypted wallets need to be created blank first.
- if (!passphrase.empty()) {
- wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
- }
// Private keys must be disabled for an external signer wallet
if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -406,7 +400,7 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
}
// Do not allow a passphrase when private keys are disabled
- if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (born_encrypted && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
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.");
status = DatabaseStatus::FAILED_CREATE;
return nullptr;
@@ -422,20 +416,15 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
// Make the wallet
context.chain->initMessage(_("Creating wallet…"));
- std::shared_ptr<CWallet> wallet = CWallet::CreateNew(context, name, std::move(database), wallet_creation_flags, error, warnings);
+ std::shared_ptr<CWallet> wallet = CWallet::CreateNew(context, name, std::move(database), wallet_creation_flags, born_encrypted, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_CREATE;
return nullptr;
}
- // Unset the blank flag if not specified by the user
- if (!create_blank) {
- wallet->UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
- }
-
// Encrypt the wallet
- if (!passphrase.empty()) {
+ if (born_encrypted) {
if (!wallet->EncryptWallet(passphrase)) {
error = Untranslated("Error: Wallet created but failed to encrypt.");
status = DatabaseStatus::FAILED_ENCRYPT;
@@ -3060,7 +3049,7 @@ bool CWallet::LoadWalletArgs(std::shared_ptr<CWallet> wallet, const WalletContex
return true;
}
-std::shared_ptr<CWallet> CWallet::CreateNew(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> CWallet::CreateNew(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bool born_encrypted, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
interfaces::Chain* chain = context.chain;
const std::string& walletFile = database->Filename();
@@ -3089,7 +3078,10 @@ std::shared_ptr<CWallet> CWallet::CreateNew(WalletContext& context, const std::s
// Only descriptor wallets can be created
assert(walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
- walletInstance->SetupWalletGeneration();
+ // Born encrypted wallets will have their keys generated later
+ if (!born_encrypted) {
+ walletInstance->SetupWalletGeneration();
+ }
if (chain) {
std::optional<int> tip_height = chain->getHeight();
@@ -4170,7 +4162,7 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
return false;
}
- data->watchonly_wallet = CWallet::CreateNew(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings);
+ data->watchonly_wallet = CWallet::CreateNew(empty_context, wallet_name, std::move(database), options.create_flags, /*born_encrypted=*/false, error, warnings);
if (!data->watchonly_wallet) {
error = _("Error: Failed to create new watchonly wallet");
return false;
@@ -4209,7 +4201,7 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
return false;
}
- data->solvable_wallet = CWallet::CreateNew(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings);
+ data->solvable_wallet = CWallet::CreateNew(empty_context, wallet_name, std::move(database), options.create_flags, /*born_encrypted=*/false, error, warnings);
if (!data->solvable_wallet) {
error = _("Error: Failed to create new watchonly wallet");
return false;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 9a1b4fbc..360b7315 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -871,7 +871,7 @@ public:
static bool LoadWalletArgs(std::shared_ptr<CWallet> wallet, const WalletContext& context, bilingual_str& error, std::vector<bilingual_str>& warnings);
/* Initializes, creates and returns a new CWallet; returns a null pointer in case of an error */
- static std::shared_ptr<CWallet> CreateNew(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
+ static std::shared_ptr<CWallet> CreateNew(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bool born_encrypted, bilingual_str& error, std::vector<bilingual_str>& warnings);
/* Initializes, loads, and returns a CWallet from an existing wallet; returns a null pointer in case of an error */
static std::shared_ptr<CWallet> LoadExisting(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, bilingual_str& error, std::vector<bilingual_str>& warnings);
Why this scored 13/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.