achow101
commented at 10:52 pm on July 21, 2025:
member
The wallet uses SQLite as a key-value store even though SQLite is a powerful relational database engine. This causes us numerous headaches due to the need to serialize multiple fields together, and since record read from the database during loading can come in any order.
Notably, for transactions, if we were able to load them in the transaction order assigned by the wallet, PRs like #27865 would be a bit simpler and easier to reason about.
This PR makes it possible for us to do that by storing transactions in a separate table. This table has a column for each field in CWalletTx, which lets us use a SQL statement like SELECT * FROM transactions ORDER BY pos which guarantees us the order in which the transactions are loaded into the wallet.
The eventual goal is to use SQLite as a relational database with tables that store our keys and other metadata that can reference each other so that the wallet doesn’t just use the database as a storage mechanism. But that is still a work in progress.
This PR makes use of the last client opened features introduced in #32895 to determine when the old record style needs to be upgraded to the new transactions table. This should give us sufficient upgrade-downgrade-upgrade handling to not lose any funds.
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.
LLM Linter (✨ experimental)
Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):
sqlite3_prepare_v2(&m_db, stmt_text.c_str(), -1, &m_stmt, nullptr) in src/wallet/sqlite.cpp
SQLiteDatabase(fs::PathFromString(“mock/”), fs::PathFromString(“mock/wallet.dat”), DatabaseOptions(), SQLITE_OPEN_MEMORY) in src/wallet/test/util.cpp
2026-01-19
DrahtBot added the label
CI failed
on Jul 22, 2025
DrahtBot
commented at 0:40 am on July 22, 2025:
contributor
🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/46429203587
LLM reason (✨ experimental): The CI failure is caused by Python lint errors due to unresolved ‘self’ references in test files.
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.
achow101 force-pushed
on Jul 22, 2025
achow101 force-pushed
on Jul 22, 2025
DrahtBot removed the label
CI failed
on Jul 22, 2025
achow101 force-pushed
on Jul 23, 2025
w0xlt
commented at 9:15 pm on July 23, 2025:
contributor
Concept ACK
achow101 force-pushed
on Jul 24, 2025
DrahtBot added the label
Needs rebase
on Jul 29, 2025
achow101 force-pushed
on Jul 29, 2025
DrahtBot removed the label
Needs rebase
on Jul 29, 2025
DrahtBot added the label
Needs rebase
on Aug 8, 2025
achow101 force-pushed
on Aug 8, 2025
DrahtBot removed the label
Needs rebase
on Aug 8, 2025
DrahtBot added the label
Needs rebase
on Aug 13, 2025
achow101 force-pushed
on Aug 14, 2025
DrahtBot removed the label
Needs rebase
on Aug 14, 2025
DrahtBot added the label
Needs rebase
on Aug 16, 2025
achow101 force-pushed
on Aug 16, 2025
achow101 force-pushed
on Aug 19, 2025
DrahtBot removed the label
Needs rebase
on Aug 19, 2025
DrahtBot added the label
Needs rebase
on Sep 23, 2025
achow101 force-pushed
on Sep 23, 2025
DrahtBot removed the label
Needs rebase
on Sep 24, 2025
rkrux
commented at 8:14 am on October 9, 2025:
contributor
Concept ACKa25557a
I have gone through the PR description and have skimmed over the commit messages as of now. At the outset, I find myself agreeing with the intent here. I am not aware of a strong enough reason to keep using SQLite as a KVS for all purposes, using its relational database properties for wallet transactions seem like a good start.
I did notice some serialisation specifics of transaction properties in a couple previous PRs that I didn’t fully understand why were required; it could be a lot cleaner and explicit in implementation if using SQLite as an RDBMS can avoid the need for doing those specific serialisations.
DrahtBot added the label
Needs rebase
on Dec 2, 2025
achow101 force-pushed
on Dec 10, 2025
DrahtBot removed the label
Needs rebase
on Dec 10, 2025
DrahtBot added the label
CI failed
on Dec 10, 2025
DrahtBot
commented at 8:15 pm on December 10, 2025:
contributor
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.
achow101 force-pushed
on Dec 10, 2025
DrahtBot removed the label
CI failed
on Dec 11, 2025
DrahtBot added the label
Needs rebase
on Dec 17, 2025
achow101 force-pushed
on Dec 22, 2025
DrahtBot removed the label
Needs rebase
on Dec 22, 2025
DrahtBot added the label
CI failed
on Dec 23, 2025
achow101 force-pushed
on Jan 3, 2026
achow101 force-pushed
on Jan 3, 2026
DrahtBot removed the label
CI failed
on Jan 3, 2026
DrahtBot added the label
Needs rebase
on Jan 19, 2026
wallet: Pass replaces_txid to CommitTransaction outside of mapValue
Instead of updating mapValue with the "replaces_txid" value and passing
the updated mapValue to CommitTransaction, pass the replaces_txid
directly to CommitTransaction which updates mapValue as necessary.
This is prepration for removing mapValue.
098e4851bc
wallet: Pass comment and comment_to to CommitTransaction
Instead of passing these by setting them in mapValue, pass them directly
to CommitTransaction.
This is preparation for removing mapValue.
438856910b
wallet: Drop mapValue from CommitTransaction
The values previously passed in mapValue are now parameters to
CommitTransaction so there is no need for mapValue to be passed.
0a4702f55f
wallet: Make CWalletTx "from" and "message" member variables
Instead of storing "from" and "message" inside of mapValue, store these
explicitly as members of CWalletTx.
810288f12c
wallet: Make CWalletTx "comment" and "to" member variables
Instead of storing "comment" and "to" inside of mapValue, store these
expliclty as members of CWalletTx.
027e704d7b
wallet: Make CWalletTx "replaces_txid" and "replaced_by_txid" member variables
Instead of storing "replaces_txid" and "replaced_by_txid" as strings inside of
mapValue, store these expliclty as members of CWalletTx.
9710b9b2a2
wallet: Drop mapValue from CWalletTx
It doesn't make sense to be storing relevant metadata variables inside
of a string map in CWalletTx. All of the fields have been pulled out
into separate members, so there is no need for mapValue to stick around.
63a3b150b6
wallet: Drop vOrderForm from CommitTransaction
This parameter is only used to pass in the "Messages" from the GUI.
Instead of making it opaque by putting those into vOrderForm, use a
specific dedicated parameter for providing the messages.
556f09fd8d
wallet: Replace CWalletTx's vOrderForm with specific fields
vOrderForm contained 2 kinds of strings: BIP 21 messages, and BIP 70
Payment Requests. Instead of having both inside of a single vOrderForm
field that is opaque, split them into separate std::vector<std::string>
to contain this metadata.
15768d0e0d
achow101 force-pushed
on Jan 19, 2026
walletdb: Decouple last client record from CLIENT_VERSION
Instead of tying the last client record with the node version, introduce
a separate wallet client version that will be increased as necessary
when new automatic upgrades are introduced.
58ce0e96a1
wallet: Introduce LastClientFeatures flags and LAST_OPENED_FEATURES record
LastClientFeatures are feature flags indicating what automatic upgrade
features supported by the last client that opened the wallet.
The LAST_OPENED_FEATURES record stores these flags and must be
set to match the exact features supported by the client that opens a
wallet.
610fa273d4
wallet: Record the supported features of the last client to decrypt a wallet22337a39cf
wallet: Set last opened and decrypted features during migration
Set these flags to avoid the automatic upgrade after migrating.
54cfacbefd
wallet: Always rewrite tx records during migration
Since loading a wallet may change some parts of tx records (e.g. adding
nOrderPos), we should rewrite the records instead of copying them so
that the automatic upgrade does not need to be performed again when the
wallet is loaded.
7d739fcb57
bench, wallet: Make WalletMigration's setup WalletBatch scoped
WalletBatch needs to be in a scope so that it is destroyed before the
database is closed during migration.
c00e12ddef
test: Make duplicating MockableDatabases use cursor and batch
Instead of directly copying the stored records map when duplicating a
MockableDatabase, use a Cursor to read the records, and a Batch to write
them into the new database. This prepares for using SQLite as the
database backend for MockableDatabase.
4a988ed098
DrahtBot added the label
CI failed
on Jan 19, 2026
DrahtBot
commented at 7:49 pm on January 19, 2026:
contributor
🚧 At least one of the CI tasks failed.
Task test max 6 ancestor commits: https://github.com/bitcoin/bitcoin/actions/runs/21148471093/job/60819287525
LLM reason (✨ experimental): Build failed: the CI cmake/make run exited with non-zero status (gmake: all target) after linking bitcoinkernel, indicating a generic build error.
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.
DrahtBot removed the label
Needs rebase
on Jan 19, 2026
wallet, bench: Use TestingSetup in CoinSelection benchmarkdb8d8bab7f
wallet: Make Mockable{Database,Batch} subclasses of SQLite classes
The mocking functionality of MockableDatabase, MockableBatch, and
MockableCursor was not really being used. These are changed to be
subclasses of their respective SQLite* classes and will use in-memory
SQLite databases so that the tests are more representative of actual
database behavior.
MockableCursor is removed as there are no overrides needed in
SQLiteCursor for the tests.
38a5670b2d
walletdb: Remove m_mock from SQLiteDatabaseee2b763d92
sqlite: Add SQLiteStatement RAII class
This class will be used to encapsulate a sqlite3_stmt
bfba6ba187
sqlite: Make Column template function4f7c372645
sqlite: Refactor ReadPragmaInteger to use SQLiteStatementda8a9af870
sqlite: Use SQLiteStatement in PRAGMA integrity_checkb099a62572
sqlite: Use SQLiteStatement in check_main_stmteea5062d5d
sqlite: Have SQLiteCursor store SQLiteStatementfba50ea6ad
sqlite: Replace remaining sqlite3_stmt usage with SQLiteStatement337752f0ac
sqlite: Refactor common writing code from WriteKey and ExecStatement
WriteKey and ExecStatement use the same code for the actual execution of
the statement. This is refactored into a separate function, also called
ExecStatement, and the original ExecStatement renamed to
ExecEraseStatement as it is only used by the erase functions.
dc2f580cb6
sqlite: Inline BindBlobToStatement and SpanFromBlob8b94ab73f1
util: Add span constructor to (W)Txidc7de7a7b5c
sqlite: Make SQLiteStatement::Bind a template function
We will need to bind data types other than blob
1c6ce965de
sqlite: Make SQLiteDatabase::Column an std::optional
To handle columns containing NULL values, Column needs to return some
value representing NULL, so make it a std::optional.
24798ff5f0
sqlite: Add additional blob types to SQLiteStatement::Column
Not all blob data types fit in ColumnBlob, so we need additional
template type requirements to match those.
3ea04e2ea4
wallet: Add Un/Serialize to TxState structs873f794ab3
wallet: Add new serialization format functions for TxState9cbaef5304
sqlite: Create a 'transactions' table if it does not exist0ca6949f46
sqlite: Add functions and statements for transactions table1e53a2cb43
sqlite: Iterate the transactions table with SQLiteCursor8c291a5d36
walletdb: Add functions to modify transactions tableb8705ca6f0
test: Include sql transactions table in DuplicateMockDatabasee7c9e23f5f
wallet: Also write to the new transactions tabled3978d6d31
wallet: Perform automatic upgrade to using the transactions table35858421d8
walletdb: Load from transactions table and use original tx for upgrade
When loading a wallet, always load from the transactions table, except
when loading a legacy wallet for migration.
The original 'tx' records are only used to upgrade to the transactions
table if an upgraded is necessary.
This is a metadata mirror of the GitHub repository
bitcoin/bitcoin.
This site is not affiliated with GitHub.
Content is generated from a GitHub metadata backup.
generated: 2026-01-22 09:13 UTC
This site is hosted by @0xB10C More mirrored repositories can be found on mirror.b10c.me