-
Notifications
You must be signed in to change notification settings - Fork 39.2k
wallet, test: Replace MockableDatabase with in-memory SQLiteDatabase #33032
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
964eafb
e7d67c9
b69f989
59484e2
037ea2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -111,8 +111,12 @@ static void SetPragma(sqlite3* db, const std::string& key, const std::string& va | |
| Mutex SQLiteDatabase::g_sqlite_mutex; | ||
| int SQLiteDatabase::g_sqlite_count = 0; | ||
|
|
||
| SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock) | ||
| : WalletDatabase(), m_mock(mock), m_dir_path(dir_path), m_file_path(fs::PathToString(file_path)), m_write_semaphore(1), m_use_unsafe_sync(options.use_unsafe_sync) | ||
| SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options) | ||
| : SQLiteDatabase(dir_path, file_path, options, /*additional_flags=*/0) | ||
| {} | ||
|
|
||
| SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, int additional_flags) | ||
| : WalletDatabase(), m_dir_path(dir_path), m_file_path(fs::PathToString(file_path)), m_write_semaphore(1), m_use_unsafe_sync(options.use_unsafe_sync) | ||
| { | ||
| { | ||
| LOCK(g_sqlite_mutex); | ||
|
|
@@ -135,7 +139,7 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa | |
| } | ||
|
|
||
| try { | ||
| Open(); | ||
| Open(additional_flags); | ||
| } catch (const std::runtime_error&) { | ||
| // If open fails, cleanup this object and rethrow the exception | ||
| Cleanup(); | ||
|
|
@@ -243,13 +247,15 @@ bool SQLiteDatabase::Verify(bilingual_str& error) | |
|
|
||
| void SQLiteDatabase::Open() | ||
| { | ||
| int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; | ||
| if (m_mock) { | ||
| flags |= SQLITE_OPEN_MEMORY; // In memory database for mock db | ||
| } | ||
| Open(/*additional_flags*/0); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nano nit: a constant "SQLITE_NO_OPEN_FLAG = 0" would be slightly more correct. |
||
| } | ||
|
|
||
| void SQLiteDatabase::Open(int additional_flags) | ||
| { | ||
| int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | additional_flags; | ||
|
|
||
| if (m_db == nullptr) { | ||
| if (!m_mock) { | ||
| if (!(flags & SQLITE_OPEN_MEMORY)) { | ||
| TryCreateDirectories(m_dir_path); | ||
| } | ||
| int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,8 @@ | |
| #include <wallet/wallet.h> | ||
| #include <wallet/walletdb.h> | ||
|
|
||
| #include <sqlite3.h> | ||
|
|
||
| #include <memory> | ||
|
|
||
| namespace wallet { | ||
|
|
@@ -105,7 +107,23 @@ void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet) | |
|
|
||
| std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database) | ||
| { | ||
| return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records); | ||
| 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<DatabaseBatch> new_db_batch = new_db->MakeBatch(); | ||
| MockableSQLiteBatch* batch_new = dynamic_cast<MockableSQLiteBatch*>(new_db_batch.get()); | ||
| Assert(batch_new); | ||
|
|
||
| while (true) { | ||
| DataStream key, value; | ||
| DatabaseCursor::Status status = cursor_orig->Next(key, value); | ||
| Assert(status != DatabaseCursor::Status::FAIL); | ||
| if (status != DatabaseCursor::Status::MORE) break; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nano nit: should be |
||
| batch_new->WriteKey(std::move(key), std::move(value)); | ||
| } | ||
|
|
||
| return new_db; | ||
| } | ||
|
|
||
| std::string getnewaddress(CWallet& w) | ||
|
|
@@ -119,103 +137,13 @@ CTxDestination getNewDestination(CWallet& w, OutputType output_type) | |
| return *Assert(w.GetNewDestination(output_type, "")); | ||
| } | ||
|
|
||
| MockableCursor::MockableCursor(const MockableData& records, bool pass, std::span<const std::byte> prefix) | ||
| { | ||
| m_pass = pass; | ||
| std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix}); | ||
| } | ||
|
|
||
| DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value) | ||
| { | ||
| if (!m_pass) { | ||
| return Status::FAIL; | ||
| } | ||
| if (m_cursor == m_cursor_end) { | ||
| return Status::DONE; | ||
| } | ||
| key.clear(); | ||
| value.clear(); | ||
| const auto& [key_data, value_data] = *m_cursor; | ||
| key.write(key_data); | ||
| value.write(value_data); | ||
| m_cursor++; | ||
| return Status::MORE; | ||
| } | ||
|
|
||
| bool MockableBatch::ReadKey(DataStream&& key, DataStream& value) | ||
| { | ||
| if (!m_pass) { | ||
| return false; | ||
| } | ||
| SerializeData key_data{key.begin(), key.end()}; | ||
| const auto& it = m_records.find(key_data); | ||
| if (it == m_records.end()) { | ||
| return false; | ||
| } | ||
| value.clear(); | ||
| value.write(it->second); | ||
| return true; | ||
| } | ||
|
|
||
| bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite) | ||
| { | ||
| if (!m_pass) { | ||
| return false; | ||
| } | ||
| SerializeData key_data{key.begin(), key.end()}; | ||
| SerializeData value_data{value.begin(), value.end()}; | ||
| auto [it, inserted] = m_records.emplace(key_data, value_data); | ||
| if (!inserted && overwrite) { // Overwrite if requested | ||
| it->second = value_data; | ||
| inserted = true; | ||
| } | ||
| return inserted; | ||
| } | ||
|
|
||
| bool MockableBatch::EraseKey(DataStream&& key) | ||
| { | ||
| if (!m_pass) { | ||
| return false; | ||
| } | ||
| SerializeData key_data{key.begin(), key.end()}; | ||
| m_records.erase(key_data); | ||
| return true; | ||
| } | ||
|
|
||
| bool MockableBatch::HasKey(DataStream&& key) | ||
| { | ||
| if (!m_pass) { | ||
| return false; | ||
| } | ||
| SerializeData key_data{key.begin(), key.end()}; | ||
| return m_records.contains(key_data); | ||
| } | ||
|
|
||
| bool MockableBatch::ErasePrefix(std::span<const std::byte> prefix) | ||
| { | ||
| if (!m_pass) { | ||
| return false; | ||
| } | ||
| auto it = m_records.begin(); | ||
| while (it != m_records.end()) { | ||
| auto& key = it->first; | ||
| if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) { | ||
| it++; | ||
| continue; | ||
| } | ||
| it = m_records.erase(it); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records) | ||
| { | ||
| return std::make_unique<MockableDatabase>(records); | ||
| } | ||
| MockableSQLiteDatabase::MockableSQLiteDatabase() | ||
| : SQLiteDatabase(fs::PathFromString("mock/"), fs::PathFromString("mock/wallet.dat"), DatabaseOptions(), SQLITE_OPEN_MEMORY) | ||
| {} | ||
|
|
||
| MockableDatabase& GetMockableDatabase(CWallet& wallet) | ||
| std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase() | ||
| { | ||
| return dynamic_cast<MockableDatabase&>(wallet.GetDatabase()); | ||
| return std::make_unique<MockableSQLiteDatabase>(); | ||
| } | ||
|
|
||
| wallet::DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: