mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-07-24 07:08:44 +02:00
wallet: use in-memory SQLite for temporary wallet in exportwatchonlywallet
The intermediate watchonly wallet created during exportwatchonlywallet is
a pure build artifact — it is always discarded once BackupWallet() copies
it to the destination. Creating it as an in-memory SQLiteDatabase
(SQLITE_OPEN_MEMORY) removes the need to write files to the wallets
directory and eliminates the cleanup handler that deleted those files on
both success and failure paths.
Introduces InMemoryWalletDatabase (a minimal SQLiteDatabase subclass) and
MakeInMemoryWalletDatabase() factory in sqlite.h/cpp, following the same
pattern as MockableSQLiteDatabase / CreateMockableWalletDatabase() in the
test utilities. MockableSQLiteDatabase now derives from InMemoryWalletDatabase,
removing its redundant Files() override.
The wallet is named after the source wallet ("<name>_watchonly_temp") so
concurrent exports of different wallets use distinct names and log lines
remain traceable to the source wallet.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <util/expected.h>
|
||||
#include <wallet/scriptpubkeyman.h>
|
||||
#include <wallet/context.h>
|
||||
#include <wallet/sqlite.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
#include <fstream>
|
||||
@@ -69,40 +70,15 @@ util::Result<std::string> ExportWatchOnlyWallet(const CWallet& wallet, const fs:
|
||||
return util::Error{_("Error: Wallet has no descriptors to export")};
|
||||
}
|
||||
|
||||
// Setup DatabaseOptions to create a new sqlite database
|
||||
DatabaseOptions options;
|
||||
options.require_existing = false;
|
||||
options.require_create = true;
|
||||
options.require_format = DatabaseFormat::SQLITE;
|
||||
|
||||
// Make the wallet with the same flags as this wallet, but without private keys
|
||||
options.create_flags = wallet.GetWalletFlags() | WALLET_FLAG_DISABLE_PRIVATE_KEYS;
|
||||
const uint64_t create_flags = wallet.GetWalletFlags() | WALLET_FLAG_DISABLE_PRIVATE_KEYS;
|
||||
|
||||
// Make the watchonly wallet
|
||||
DatabaseStatus status;
|
||||
// Create the temporary watchonly wallet in memory to avoid leaving files on disk
|
||||
std::vector<bilingual_str> warnings;
|
||||
std::string wallet_name = wallet.GetName() + "_watchonly_temp";
|
||||
bilingual_str error;
|
||||
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);
|
||||
if (!database) {
|
||||
return util::Error{strprintf(_("Wallet file creation failed: %s"), error)};
|
||||
}
|
||||
|
||||
// Always remove the temporary wallet files, even when returning early on error.
|
||||
std::shared_ptr<CWallet> watchonly_wallet;
|
||||
fs::path wallet_path = fs::PathFromString(database->Filename()).parent_path();
|
||||
std::vector<fs::path> cleanup_files = database->Files();
|
||||
auto cleanup_watchonly_wallet = interfaces::MakeCleanupHandler([&watchonly_wallet, &wallet_path, &cleanup_files] {
|
||||
if (watchonly_wallet) watchonly_wallet.reset();
|
||||
for (const auto& file : cleanup_files) {
|
||||
fs::remove(file);
|
||||
}
|
||||
fs::remove(wallet_path);
|
||||
});
|
||||
|
||||
WalletContext empty_context;
|
||||
empty_context.args = context.args;
|
||||
watchonly_wallet = CWallet::CreateNew(empty_context, wallet_name, std::move(database), options.create_flags, /*born_encrypted=*/false, error, warnings);
|
||||
std::shared_ptr<CWallet> watchonly_wallet = CWallet::CreateNew(empty_context, /*name=*/wallet.GetName() + "_watchonly_temp", MakeInMemoryWalletDatabase(), create_flags, /*born_encrypted=*/false, error, warnings);
|
||||
if (!watchonly_wallet) {
|
||||
return util::Error{strprintf(_("Error: Failed to create new watchonly wallet. %s"), error)};
|
||||
}
|
||||
|
||||
@@ -722,6 +722,15 @@ std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const D
|
||||
}
|
||||
}
|
||||
|
||||
InMemoryWalletDatabase::InMemoryWalletDatabase()
|
||||
: SQLiteDatabase(fs::path{}, fs::path{":memory:"}, DatabaseOptions(), SQLITE_OPEN_MEMORY)
|
||||
{}
|
||||
|
||||
std::unique_ptr<WalletDatabase> MakeInMemoryWalletDatabase()
|
||||
{
|
||||
return std::make_unique<InMemoryWalletDatabase>();
|
||||
}
|
||||
|
||||
std::string SQLiteDatabaseVersion()
|
||||
{
|
||||
return std::string(sqlite3_libversion());
|
||||
|
||||
@@ -175,8 +175,19 @@ public:
|
||||
bool m_use_unsafe_sync;
|
||||
};
|
||||
|
||||
/** An in-memory SQLiteDatabase. Used as a temporary build artifact where no
|
||||
* on-disk persistence is needed. */
|
||||
class InMemoryWalletDatabase : public SQLiteDatabase
|
||||
{
|
||||
public:
|
||||
InMemoryWalletDatabase();
|
||||
std::vector<fs::path> Files() override { return {}; }
|
||||
};
|
||||
|
||||
std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
|
||||
|
||||
std::unique_ptr<WalletDatabase> MakeInMemoryWalletDatabase();
|
||||
|
||||
std::string SQLiteDatabaseVersion();
|
||||
} // namespace wallet
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ CTxDestination getNewDestination(CWallet& w, OutputType output_type)
|
||||
}
|
||||
|
||||
MockableSQLiteDatabase::MockableSQLiteDatabase()
|
||||
: SQLiteDatabase(fs::PathFromString("mock/"), fs::PathFromString("mock/wallet.dat"), DatabaseOptions(), SQLITE_OPEN_MEMORY)
|
||||
: InMemoryWalletDatabase()
|
||||
{}
|
||||
|
||||
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase()
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
/** A WalletDatabase whose contents and return values can be modified as needed for testing
|
||||
**/
|
||||
class MockableSQLiteDatabase : public SQLiteDatabase
|
||||
class MockableSQLiteDatabase : public InMemoryWalletDatabase
|
||||
{
|
||||
public:
|
||||
MockableSQLiteDatabase();
|
||||
@@ -64,7 +64,6 @@ public:
|
||||
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 "sqlite-mock"; }
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableSQLiteBatch>(*this); }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user