Skip to content

Conversation

achow101
Copy link
Member

MockableDatabase was introduced for the tests to avoid tying non-database tests to a particular database type. However, since the only database type now is sqlite, and because the mockable behavior is no longer used by the tests, we can replace usage of the MockabeDatabase with a SQLite database that lives only in memory.

This is particularly useful for future work that has the wallet make use of SQLite's capabilities more, which are less conducive to having a separate mock database implementation.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 21, 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/33032.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK rkrux
Concept ACK brunoerg
Stale ACK PeterWrighten

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #33034 (wallet: Store transactions in a separate sqlite table by achow101)
  • #32685 (wallet: Allow read-only database access for info and dump commands by PeterWrighten)

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.

@PeterWrighten
Copy link

PeterWrighten commented Jul 22, 2025

utACK 7889750

achow101 added 2 commits July 22, 2025 17:03
WalletBatch needs to be in a scope so that it is destroyed before the
database is closed during migration.
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.
@achow101 achow101 force-pushed the sqlite-in-memory-db branch from 7889750 to f698524 Compare July 23, 2025 00:05
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 f698524

I have looked in depth only the last commit. Almost all of my suggestions merge towards having child mock class(es) extending the base SQLite specific classes, wherever required. They can be deleted in the future if the need arises without touching the base SQLite classes. Also, I believe they would not make it difficult to achieve the following goal stated in the PR desc.

This is particularly useful for future work that has the wallet make use of SQLite's capabilities more, which are less conducive to having a separate mock database implementation.

@@ -3448,7 +3448,7 @@ void CWallet::SetupLegacyScriptPubKeyMan()
return;
}

Assert(m_database->Format() == "bdb_ro" || m_database->Format() == "mock");
Assert(m_database->Format() == "bdb_ro" || m_database->Format() == "sqlite-mock");
Copy link
Contributor

Choose a reason for hiding this comment

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

In f698524 "test, wallet: Replace MockableDatabase with in-memory SQLiteDatabase"

Nit and need not be done in this PR: To avoid hardcoding mocking constants in non-test code, perhaps the following? If the return value of Format had been an enum, the following would come across as a tighter check.

- Assert(m_database->Format() == "bdb_ro" || m_database->Format() == "sqlite-mock");
+ Assert(m_database->Format() != "sqlite"); 

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer to leave this as-is as it is a more specific check. It checks for the db types that are allowed to be legacy wallets, rather than allowing legacy wallets for all non-sqlite databases.

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.
@achow101 achow101 force-pushed the sqlite-in-memory-db branch from f698524 to 98bb7ef Compare July 24, 2025 19:24
Copy link
Contributor

@brunoerg brunoerg 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

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Task MSan, depends: https://github.com/bitcoin/bitcoin/runs/46676643655
LLM reason (✨ experimental): The CI failure is caused by a compilation error due to the use of the -Werror flag, which treats a warning about an unused private field as an error, preventing successful build.

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

@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.

tACK 1cdb916

Thanks for incorporating suggestions.

Comment on lines 83 to 91
std::unique_ptr<DatabaseBatch> batch_orig = database.MakeBatch();
std::unique_ptr<DatabaseCursor> cursor_orig = batch_orig->GetNewCursor();

std::unique_ptr<WalletDatabase> new_db = std::make_unique<MockableDatabase>();
std::unique_ptr<WalletDatabase> new_db = CreateMockableWalletDatabase();
std::unique_ptr<DatabaseBatch> new_db_batch = new_db->MakeBatch();
MockableBatch* batch_new = dynamic_cast<MockableBatch*>(new_db_batch.get());
MockableSQLiteBatch* batch_new = dynamic_cast<MockableSQLiteBatch*>(new_db_batch.get());
Assert(batch_new);

while (true) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit to use tighter function signature that goes along with the function name too. Builds & tests fine on my system.

Use MockableSQLiteDatabase in return type that passes for WalletDatabase
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
index 6017582395..58fafeecf7 100644
--- a/src/wallet/test/util.cpp
+++ b/src/wallet/test/util.cpp
@@ -80,12 +80,12 @@ void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
     WaitForDeleteWallet(std::move(wallet));
 }
 
-std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
+std::unique_ptr<MockableSQLiteDatabase> DuplicateMockDatabase(MockableSQLiteDatabase& database)
 {
     std::unique_ptr<DatabaseBatch> batch_orig = database.MakeBatch();
     std::unique_ptr<DatabaseCursor> cursor_orig = batch_orig->GetNewCursor();
 
-    std::unique_ptr<WalletDatabase> new_db = CreateMockableWalletDatabase();
+    std::unique_ptr<MockableSQLiteDatabase> new_db = CreateMockableWalletDatabase();
     std::unique_ptr<DatabaseBatch> new_db_batch = new_db->MakeBatch();
     MockableSQLiteBatch* batch_new = dynamic_cast<MockableSQLiteBatch*>(new_db_batch.get());
     Assert(batch_new);
@@ -116,7 +116,7 @@ MockableSQLiteDatabase::MockableSQLiteDatabase()
     : SQLiteDatabase(fs::PathFromString("mock/"), fs::PathFromString("mock/wallet.dat"), DatabaseOptions(), SQLITE_OPEN_MEMORY)
 {}
 
-std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase()
+std::unique_ptr<MockableSQLiteDatabase> CreateMockableWalletDatabase()
 {
     return std::make_unique<MockableSQLiteDatabase>();
 }
diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h
index a240a8ae4f..cedd37ee7e 100644
--- a/src/wallet/test/util.h
+++ b/src/wallet/test/util.h
@@ -70,7 +70,7 @@ public:
     std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableSQLiteBatch>(*this); }
 };
 
-std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase();
+std::unique_ptr<MockableSQLiteDatabase> CreateMockableWalletDatabase();
 MockableSQLiteDatabase& GetMockableDatabase(CWallet& wallet);
 
 DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants