wallet: add option to create in-memory SQLite DB in the migration

This commit is contained in:
brunoerg 2024-12-10 17:52:19 -03:00
parent f409444d02
commit ba8d151fe0
6 changed files with 20 additions and 13 deletions

View file

@ -63,7 +63,7 @@ static void WalletMigration(benchmark::Bench& bench)
bench.epochs(/*numEpochs=*/1).epochIterations(/*numIters=*/1) // run the migration exactly once bench.epochs(/*numEpochs=*/1).epochIterations(/*numIters=*/1) // run the migration exactly once
.run([&] { .run([&] {
auto res{MigrateLegacyToDescriptor(std::move(wallet), /*passphrase=*/"", *loader->context(), /*was_loaded=*/false)}; auto res{MigrateLegacyToDescriptor(std::move(wallet), /*passphrase=*/"", *loader->context(), /*was_loaded=*/false, /*in_memory=*/false)};
assert(res); assert(res);
assert(res->wallet); assert(res->wallet);
assert(res->watchonly_wallet); assert(res->watchonly_wallet);

View file

@ -184,12 +184,13 @@ public:
enum class DatabaseFormat { enum class DatabaseFormat {
SQLITE, SQLITE,
BERKELEY_RO, BERKELEY_RO,
BERKELEY_SWAP, BERKELEY_SWAP
}; };
struct DatabaseOptions { struct DatabaseOptions {
bool require_existing = false; bool require_existing = false;
bool require_create = false; bool require_create = false;
bool in_memory = false;
std::optional<DatabaseFormat> require_format; std::optional<DatabaseFormat> require_format;
uint64_t create_flags = 0; uint64_t create_flags = 0;
SecureString create_passphrase; SecureString create_passphrase;

View file

@ -695,7 +695,7 @@ std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const D
{ {
try { try {
fs::path data_file = SQLiteDataFile(path); fs::path data_file = SQLiteDataFile(path);
auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file, options); auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file, options, options.in_memory ? true : false);
if (options.verify && !db->Verify(error)) { if (options.verify && !db->Verify(error)) {
status = DatabaseStatus::FAILED_VERIFY; status = DatabaseStatus::FAILED_VERIFY;
return nullptr; return nullptr;

View file

@ -4010,7 +4010,7 @@ util::Result<ScriptPubKeyMan*> CWallet::AddWalletDescriptor(WalletDescriptor& de
return spk_man; return spk_man;
} }
bool CWallet::MigrateToSQLite(bilingual_str& error) bool CWallet::MigrateToSQLite(bilingual_str& error, bool in_memory)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
@ -4061,6 +4061,7 @@ bool CWallet::MigrateToSQLite(bilingual_str& error)
DatabaseOptions opts; DatabaseOptions opts;
opts.require_create = true; opts.require_create = true;
opts.require_format = DatabaseFormat::SQLITE; opts.require_format = DatabaseFormat::SQLITE;
opts.in_memory = in_memory;
DatabaseStatus db_status; DatabaseStatus db_status;
std::unique_ptr<WalletDatabase> new_db = MakeDatabase(wallet_path, opts, db_status, error); std::unique_ptr<WalletDatabase> new_db = MakeDatabase(wallet_path, opts, db_status, error);
assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists. assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists.
@ -4476,10 +4477,10 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error}; return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error};
} }
return MigrateLegacyToDescriptor(std::move(local_wallet), passphrase, context, was_loaded); return MigrateLegacyToDescriptor(std::move(local_wallet), passphrase, context, was_loaded, /*in_memory=*/false);
} }
util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet> local_wallet, const SecureString& passphrase, WalletContext& context, bool was_loaded) util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet> local_wallet, const SecureString& passphrase, WalletContext& context, bool was_loaded, bool in_memory)
{ {
MigrationResult res; MigrationResult res;
bilingual_str error; bilingual_str error;
@ -4541,7 +4542,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
{ {
LOCK(local_wallet->cs_wallet); LOCK(local_wallet->cs_wallet);
// First change to using SQLite // First change to using SQLite
if (!local_wallet->MigrateToSQLite(error)) return util::Error{error}; if (!local_wallet->MigrateToSQLite(error, in_memory)) return util::Error{error};
// Do the migration of keys and scripts for non-empty wallets, and cleanup if it fails // Do the migration of keys and scripts for non-empty wallets, and cleanup if it fails
if (HasLegacyRecords(*local_wallet)) { if (HasLegacyRecords(*local_wallet)) {
@ -4580,7 +4581,10 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
// Migration failed, cleanup // Migration failed, cleanup
// Before deleting the wallet's directory, copy the backup file to the top-level wallets dir // Before deleting the wallet's directory, copy the backup file to the top-level wallets dir
fs::path temp_backup_location = fsbridge::AbsPathJoin(GetWalletDir(), backup_filename); fs::path temp_backup_location = fsbridge::AbsPathJoin(GetWalletDir(), backup_filename);
if (!in_memory) {
fs::copy_file(backup_path, temp_backup_location, fs::copy_options::none); fs::copy_file(backup_path, temp_backup_location, fs::copy_options::none);
}
// Make list of wallets to cleanup // Make list of wallets to cleanup
std::vector<std::shared_ptr<CWallet>> created_wallets; std::vector<std::shared_ptr<CWallet>> created_wallets;
@ -4625,8 +4629,10 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
} }
// The wallet directory has been restored, but just in case, copy the previously created backup to the wallet dir // The wallet directory has been restored, but just in case, copy the previously created backup to the wallet dir
if (!in_memory) {
fs::copy_file(temp_backup_location, backup_path, fs::copy_options::none); fs::copy_file(temp_backup_location, backup_path, fs::copy_options::none);
fs::remove(temp_backup_location); fs::remove(temp_backup_location);
}
// Verify that there is no dangling wallet: when the wallet wasn't loaded before, expect null. // Verify that there is no dangling wallet: when the wallet wasn't loaded before, expect null.
// This check is performed after restoration to avoid an early error before saving the backup. // This check is performed after restoration to avoid an early error before saving the backup.

View file

@ -1046,7 +1046,7 @@ public:
* A backup is not created. * A backup is not created.
* May crash if something unexpected happens in the filesystem. * May crash if something unexpected happens in the filesystem.
*/ */
bool MigrateToSQLite(bilingual_str& error) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool MigrateToSQLite(bilingual_str& error, bool in_memory = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Get all of the descriptors from a legacy wallet //! Get all of the descriptors from a legacy wallet
std::optional<MigrationData> GetDescriptorsForLegacy(bilingual_str& error) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::optional<MigrationData> GetDescriptorsForLegacy(bilingual_str& error) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -1137,7 +1137,7 @@ struct MigrationResult {
//! Do all steps to migrate a legacy wallet to a descriptor wallet //! Do all steps to migrate a legacy wallet to a descriptor wallet
[[nodiscard]] util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context); [[nodiscard]] util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context);
//! Requirement: The wallet provided to this function must be isolated, with no attachment to the node's context. //! Requirement: The wallet provided to this function must be isolated, with no attachment to the node's context.
[[nodiscard]] util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet> local_wallet, const SecureString& passphrase, WalletContext& context, bool was_loaded); [[nodiscard]] util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet> local_wallet, const SecureString& passphrase, WalletContext& context, bool was_loaded, bool in_memory);
} // namespace wallet } // namespace wallet
#endif // BITCOIN_WALLET_WALLET_H #endif // BITCOIN_WALLET_WALLET_H

View file

@ -1427,7 +1427,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
{ {
bool exists; bool exists;
try { try {
exists = fs::symlink_status(path).type() != fs::file_type::not_found; exists = fs::symlink_status(path).type() != fs::file_type::not_found && !options.in_memory;
} catch (const fs::filesystem_error& e) { } catch (const fs::filesystem_error& e) {
error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e))); error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e)));
status = DatabaseStatus::FAILED_BAD_PATH; status = DatabaseStatus::FAILED_BAD_PATH;