Skip to content

Conversation

@davidgumberg
Copy link
Contributor

@davidgumberg davidgumberg commented May 29, 2025

This PR is mostly a refactor which splits out logic used for creating wallets and for loading wallets, both of which are presently contained in CWallet::Create() into CWallet::CreateNew() and CWallet::LoadExisting()

The real win of this PR is that CWallet::Create() uses a very bad heuristic for trying to guess whether or not it is supposed to be creating a new wallet or loading an existing wallet:

bitcoin/src/wallet/wallet.cpp

Lines 2882 to 2885 in 370c592

// 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);

This heuristic assumes that wallets with no ScriptPubKeyMans are being created, which sounds reasonable, but as demonstrated in #32112 and #32111, this can happen when the user tries to load a wallet file that is corrupted, both issues are fixed by this PR and any other misbehavior for wallet files which succeeded the broken heuristic's sniff test for new wallets.

It was already the case that every caller of CWallet::Create() knows whether it is creating a wallet or loading one, so we can avoid replacing this bad heuristic with another one, and just shift the burden to the caller.

@DrahtBot
Copy link
Contributor

DrahtBot commented May 29, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32636.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK w0xlt
Concept ACK pablomartin4btc, rkrux, enirox001

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #34193 (wallet: make migration more robust against failures by furszy)
  • #33034 (wallet: Store transactions in a separate sqlite table by achow101)
  • #32895 (wallet: Prepare for future upgrades by recording versions of last client to open and decrypt by achow101)
  • #32685 (wallet: Allow read-only database access for info and dump commands by PeterWrighten)
  • #32138 (wallet, rpc: remove settxfee and paytxfee by polespinasa)
  • #19461 (multiprocess: Add bitcoin-gui -ipcconnect option by ryanofsky)
  • #19460 (multiprocess: Add bitcoin-wallet -ipcconnect option by ryanofsky)
  • #10102 (Multiprocess bitcoin by ryanofsky)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach ACK

@achow101
Copy link
Member

PopulateWalletFromDB inside of CreateNew should no longer be necessary.

It'd be nice if the load or create intention could be propagated down to the DB level as well. In SQLiteDatabase::Open, we pass the flags SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE which does both load and create. I think ideally it would be just SQLITE_OPEN_READWRITE for loading, and SQLITE_OPEN_CREATE for creating to further enforce that we expect a file to already exist for loading, and for no files to exist when creating. Similarly, we could avoid calling TryCreateDirectories when loading.

@achow101 achow101 added the Wallet label Jun 4, 2025
@luke-jr
Copy link
Member

luke-jr commented Jun 6, 2025

Any reason not to just pass a parameter to CWallet::Create, just to fix the bug in the simplest manner? Refactoring should ideally be separate from fixing.

@achow101
Copy link
Member

achow101 commented Jun 6, 2025

Any reason not to just pass a parameter to CWallet::Create, just to fix the bug in the simplest manner? Refactoring should ideally be separate from fixing.

Because doing the bare minimum to fix a bug that is unreachable in production is how we end up with technical debt.

IMO, this PR is primarily a refactor to split these 2 actions that should not be combined. The fact that it fixes those bugs, which are a result of the combining, is a bonus.

Copy link
Member

@pablomartin4btc pablomartin4btc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK

The current state of this code in master adds confusion to flows like the legacy to descriptor wallet migration that's why I think it's an important work to be done.

Copy link
Contributor

@rkrux rkrux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK 0f82224d3937db3f60cfaec249ac5fe3264dc3d5
I want to share my initial thoughts before I invest more time reviewing this.


I am in favour of such a refactor because from a reviewing POV I lose mental cycles every time I end up going through CWallet:Create function that requires me to keep a mental context of whether this function was called from the creation flow or the load flow. Also, I feel this is a good time to get this done now that legacy wallets are no longer there.

However, I am slightly less inclined to review this PR in its current format because there are a few moving pieces here, and I'm afraid reviewing it thoroughly might be more time consuming and would induce less confidence in me finally a-c-king it.

Moreover, I feel the benefits of such a refactor would be more highlighted and appreciated if this can be broken down into smaller PRs in addition to ensuring data-correctness. Couple reasons I'm suggesting a breakdown are:

  1. I would like to give more attention to each of the commits by trying to understand their consequences on the wallet as a whole.
  2. I don't suppose all the commits are required to be present sequentially in a single PR based on my initial look, please feel free to correct me if I'm mistaken about their ability to be present in parallel.
PR breakdown suggestion

The first 3 can be done in parallel & the last one might be dependent on the previous ones:

  1. The PopulateWalletFromDB related changes can be 1 PR - the first 2 commits.
  2. The fBroadcastTransactions change is independent, can be clubbed with the birth-time-update-removal optimisation - 3rd, 6th commits.
  3. The argument parsing changes also seem independent to me and can be another PR - 4th, 5th commits.
  4. No strong opinion on the wallet stats logging commit, can go anywhere.
  5. The last 5 commits can be 1 PR that introduces and uses the LoadExisting & CreateNew functions.

Also, I believe this is a good suggestion that might give dividends in the future and breaking down into smaller PRs might make it easy to incorporate it.

Having suggested all this, if you think the reward for breaking down the PR is not enough because the smaller refactoring PRs might not attract reviewers or might add rebase churn, I would still review this PR later but some kind of breakdown would aid review if not the one that I suggested.

Sorry if this got too long.

@pablomartin4btc
Copy link
Member

Shouldn't src/wallet/walletdb.cpp be part of this PR? DBErrors WalletBatch::LoadWallet(CWallet* pwallet) is being called during wallet creation I think. If so, please also consider the note at #33082's description.

@davidgumberg davidgumberg force-pushed the 5-27-2025-create-refactor branch from 0f82224 to 349c9bc Compare August 23, 2025 02:07
@davidgumberg davidgumberg force-pushed the 5-27-2025-create-refactor branch from 349c9bc to 353cfdd Compare August 23, 2025 05:09
@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/48721895167
LLM reason (✨ experimental): The CI failure is caused by a linting error detected by ruff due to an unused import in the test file, preventing successful completion of the lint check.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

@davidgumberg davidgumberg force-pushed the 5-27-2025-create-refactor branch from 353cfdd to 05cbadf Compare August 23, 2025 06:01
davidgumberg added a commit to davidgumberg/bitcoin that referenced this pull request Jan 16, 2026
Previously creating an encrypted wallet would result in the keypool size
incorrectly being reported as 0.

See: bitcoin#32636 (comment)
davidgumberg added a commit to davidgumberg/bitcoin that referenced this pull request Jan 16, 2026
Previously creating an encrypted wallet would result in the keypool size
incorrectly being reported as 0.

See: bitcoin#32636 (comment)
@davidgumberg davidgumberg force-pushed the 5-27-2025-create-refactor branch from b29d22a to a3e24b0 Compare January 16, 2026 03:02
@davidgumberg
Copy link
Contributor Author

Pushed an update to this branch to address @rkrux's feedback, and feedback from an offline review with @achow101 and @w0xlt.

  • I've dropped an unnecessary overloaded version of PopulateWalletFromDB() that doesn't take an error and warnings argument, deleting some users of PopulateWalletFromDB() and creating dummy variables for another user.
  • Significantly improved commit messasge for 1c4f17e.

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/actions/runs/21054134650/job/60546651078
LLM reason (✨ experimental): Lint failure: includes_build_config flagged an unused include of bitcoin-build-config.h.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK a3e24b0
The PR executes a good refactoring and changes the function names to declare their intent (create vs load) .
The identified issues (WriteVersion parameter, public PopulateWalletFromDB) are minor and appropriate for follow-up PRs.
The CI error is unrelated.

@davidgumberg davidgumberg force-pushed the 5-27-2025-create-refactor branch 2 times, most recently from 1bcff32 to cd47b5d Compare January 16, 2026 19:15
davidgumberg added a commit to davidgumberg/bitcoin that referenced this pull request Jan 16, 2026
Previously creating an encrypted wallet would result in the keypool size
incorrectly being reported as 0.

See: bitcoin#32636 (comment)
davidgumberg and others added 14 commits January 16, 2026 11:29
Co-authored-by: @w0xlt <w0xlt@users.noreply.github.com>
Modifying `fBroadcastTransactions` does not require any locks,
initialization of this wallet parameter can be relocated with all of the
other argument parsing in this function.
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`
`m_keypool_size` must be set before `CWallet::PopulateWalletFromDB()`,
in order to move parsing of `-keypool` into `CWallet::LoadWalletArgs`,
`LoadWalletArgs()` invocation in `CWallet::Create()` must be moved
before `PopulateWalletFromDB()` is called.
Checking every SPKM in `CWallet::Create()` is not necessary, since the
only way presently for an SPKM to get added to `m_spk_managers` (the
return value of `GetAllScriptPubKeyMans()`) is through
`AddScriptPubKeyMan()`, which already invokes `MaybeUpdateBirthTime()`.
This will avoid repetition when wallet creation and loading are
separated.
Splits out logic relevant only to existing wallets in
`CWallet::Create()` into `CWallet::LoadExisting()`
…mDB()`

Writing the wallet's `CLIENT_VERSION` (which indicates the last version
to have touched a wallet) needs to be done on both wallet creation and
wallet loading.

The next commit removes the `PopulateWalletFromDatabase()` call from
wallet creation, but this behavior needs to be preserved, so this commit
factors setting `CLIENT_VERSION` out of `PopulateWalletFromDatabase()`
so that wallet creation can use it in the next commit.
Previously creating an encrypted wallet would result in the keypool size
incorrectly being reported as 0.

See: bitcoin#32636 (comment)
Aside from being more legible, changing the name of `CWallet::Create()`
also validates that every instance where a new wallet is `Create()`'ed
is handled in this branch.

-BEGIN VERIFY SCRIPT-
sed -i 's|\bCreate(|CreateNew(|g' src/wallet/wallet.cpp  src/wallet/wallet.h  src/wallet/test/util.cpp src/wallet/test/wallet_tests.cpp
-END VERIFY SCRIPT-
@davidgumberg davidgumberg force-pushed the 5-27-2025-create-refactor branch from cd47b5d to f9de11f Compare January 16, 2026 19:30
@davidgumberg
Copy link
Contributor Author

Please excuse the mess I've made...

I've pushed (a3e24b0..f9de11f) to change one line which is deleting the build-config.h include in wallettool.cpp since I drop the last usage of build-config.h in that file, satisfying IWYU linter:

$ git diff a3e24b0 f9de11f
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index b59308bae9..e0de4fc958 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -2,8 +2,6 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.

-#include <bitcoin-build-config.h> // IWYU pragma: keep
-
 #include <wallet/wallettool.h>

 #include <common/args.h>

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reACK f9de11f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants