mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-01 00:34:01 +02:00
Merge bitcoin/bitcoin#33032: wallet, test: Replace MockableDatabase with in-memory SQLiteDatabase
037ea2c714walletdb: Remove m_mock from SQLiteDatabase (Ava Chow)59484e2fdbwallet: Make Mockable{Database,Batch} subclasses of SQLite classes (Ava Chow)b69f989dc5wallet, bench: Use TestingSetup in CoinSelection benchmark (Ava Chow)e7d67c9fd9test: Make duplicating MockableDatabases use cursor and batch (Ava Chow)964eafb71cbench, wallet: Make WalletMigration's setup WalletBatch scoped (Ava Chow) Pull request description: `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. ACKs for top commit: brunoerg: code review ACK037ea2c714sedited: Re-ACK037ea2c714furszy: Code review ACK037ea2c714Tree-SHA512: 0a99c27ef4e590966b3af929bf3acf99666861905aabf150fe5660ea07c881a49935a4e7dcd676dcd5e70616898d89d872b6e156ae9c600de1361c1b2469b64d
This commit is contained in:
@@ -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;
|
||||
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});
|
||||
}
|
||||
MockableSQLiteDatabase::MockableSQLiteDatabase()
|
||||
: SQLiteDatabase(fs::PathFromString("mock/"), fs::PathFromString("mock/wallet.dat"), DatabaseOptions(), SQLITE_OPEN_MEMORY)
|
||||
{}
|
||||
|
||||
DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
|
||||
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
MockableDatabase& GetMockableDatabase(CWallet& wallet)
|
||||
{
|
||||
return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
|
||||
return std::make_unique<MockableSQLiteDatabase>();
|
||||
}
|
||||
|
||||
wallet::DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <addresstype.h>
|
||||
#include <wallet/db.h>
|
||||
#include <wallet/scriptpubkeyman.h>
|
||||
#include <wallet/sqlite.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -48,76 +49,31 @@ CTxDestination getNewDestination(CWallet& w, OutputType output_type);
|
||||
|
||||
using MockableData = std::map<SerializeData, SerializeData, std::less<>>;
|
||||
|
||||
class MockableCursor: public DatabaseCursor
|
||||
|
||||
class MockableSQLiteBatch : public SQLiteBatch
|
||||
{
|
||||
public:
|
||||
MockableData::const_iterator m_cursor;
|
||||
MockableData::const_iterator m_cursor_end;
|
||||
bool m_pass;
|
||||
|
||||
explicit MockableCursor(const MockableData& records, bool pass) : m_cursor(records.begin()), m_cursor_end(records.end()), m_pass(pass) {}
|
||||
MockableCursor(const MockableData& records, bool pass, std::span<const std::byte> prefix);
|
||||
~MockableCursor() = default;
|
||||
|
||||
Status Next(DataStream& key, DataStream& value) override;
|
||||
};
|
||||
|
||||
class MockableBatch : public DatabaseBatch
|
||||
{
|
||||
private:
|
||||
MockableData& m_records;
|
||||
bool m_pass;
|
||||
|
||||
bool ReadKey(DataStream&& key, DataStream& value) override;
|
||||
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override;
|
||||
bool EraseKey(DataStream&& key) override;
|
||||
bool HasKey(DataStream&& key) override;
|
||||
bool ErasePrefix(std::span<const std::byte> prefix) override;
|
||||
|
||||
public:
|
||||
explicit MockableBatch(MockableData& records, bool pass) : m_records(records), m_pass(pass) {}
|
||||
~MockableBatch() = default;
|
||||
|
||||
void Close() override {}
|
||||
|
||||
std::unique_ptr<DatabaseCursor> GetNewCursor() override
|
||||
{
|
||||
return std::make_unique<MockableCursor>(m_records, m_pass);
|
||||
}
|
||||
std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(std::span<const std::byte> prefix) override {
|
||||
return std::make_unique<MockableCursor>(m_records, m_pass, prefix);
|
||||
}
|
||||
bool TxnBegin() override { return m_pass; }
|
||||
bool TxnCommit() override { return m_pass; }
|
||||
bool TxnAbort() override { return m_pass; }
|
||||
bool HasActiveTxn() override { return false; }
|
||||
using SQLiteBatch::SQLiteBatch;
|
||||
using SQLiteBatch::WriteKey;
|
||||
};
|
||||
|
||||
/** A WalletDatabase whose contents and return values can be modified as needed for testing
|
||||
**/
|
||||
class MockableDatabase : public WalletDatabase
|
||||
class MockableSQLiteDatabase : public SQLiteDatabase
|
||||
{
|
||||
public:
|
||||
MockableData m_records;
|
||||
bool m_pass{true};
|
||||
MockableSQLiteDatabase();
|
||||
|
||||
MockableDatabase(MockableData records = {}) : WalletDatabase(), m_records(records) {}
|
||||
~MockableDatabase() = default;
|
||||
|
||||
void Open() override {}
|
||||
|
||||
bool Rewrite() override { return m_pass; }
|
||||
bool Backup(const std::string& strDest) const override { return m_pass; }
|
||||
void Close() override {}
|
||||
bool Backup(const std::string& strDest) const override { return true; }
|
||||
|
||||
std::string Filename() override { return "mockable"; }
|
||||
std::vector<fs::path> Files() override { return {}; }
|
||||
std::string Format() override { return "mock"; }
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableBatch>(m_records, m_pass); }
|
||||
std::string Format() override { return "sqlite-mock"; }
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableSQLiteBatch>(*this); }
|
||||
};
|
||||
|
||||
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records = {});
|
||||
MockableDatabase& GetMockableDatabase(CWallet& wallet);
|
||||
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase();
|
||||
MockableSQLiteDatabase& GetMockableDatabase(CWallet& wallet);
|
||||
|
||||
DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, bool success);
|
||||
} // namespace wallet
|
||||
|
||||
Reference in New Issue
Block a user