diff --git a/src/bench/wallet_migration.cpp b/src/bench/wallet_migration.cpp index eff6c6b526d..524839e3871 100644 --- a/src/bench/wallet_migration.cpp +++ b/src/bench/wallet_migration.cpp @@ -5,6 +5,7 @@ #include // IWYU pragma: keep #include +#include #include #include #include @@ -16,7 +17,7 @@ #include -#if defined(USE_BDB) && defined(USE_SQLITE) // only enable benchmark when bdb and sqlite are enabled +#if defined(USE_SQLITE) // only enable benchmark when sqlite is enabled namespace wallet{ @@ -32,41 +33,39 @@ static void WalletMigration(benchmark::Bench& bench) int NUM_WATCH_ONLY_ADDR = 20; // Setup legacy wallet - DatabaseOptions options; - options.use_unsafe_sync = true; - options.verify = false; - DatabaseStatus status; - bilingual_str error; - auto database = MakeWalletDatabase(fs::PathToString(test_setup->m_path_root / "legacy"), options, status, error); - uint64_t create_flags = 0; - auto wallet = TestLoadWallet(std::move(database), context, create_flags); + std::unique_ptr wallet = std::make_unique(test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()); + wallet->chainStateFlushed(ChainstateRole::NORMAL, CBlockLocator{}); + LegacyDataSPKM* legacy_spkm = wallet->GetOrCreateLegacyDataSPKM(); // Add watch-only addresses std::vector scripts_watch_only; for (int w = 0; w < NUM_WATCH_ONLY_ADDR; ++w) { CKey key = GenerateRandomKey(); LOCK(wallet->cs_wallet); - const CScript& script = scripts_watch_only.emplace_back(GetScriptForDestination(GetDestinationForKey(key.GetPubKey(), OutputType::LEGACY))); - bool res = wallet->ImportScriptPubKeys(strprintf("watch_%d", w), {script}, - /*have_solving_data=*/false, /*apply_label=*/true, /*timestamp=*/1); - assert(res); + const auto& dest = GetDestinationForKey(key.GetPubKey(), OutputType::LEGACY); + const CScript& script = scripts_watch_only.emplace_back(GetScriptForDestination(dest)); + assert(legacy_spkm->LoadWatchOnly(script)); + assert(wallet->SetAddressBook(dest, strprintf("watch_%d", w), /*purpose=*/std::nullopt)); } // Generate transactions and local addresses - for (int j = 0; j < 400; ++j) { + for (int j = 0; j < 500; ++j) { + CKey key = GenerateRandomKey(); + CPubKey pubkey = key.GetPubKey(); + // Load key, scripts and create address book record + Assert(legacy_spkm->LoadKey(key, pubkey)); + CTxDestination dest{PKHash(pubkey)}; + Assert(wallet->SetAddressBook(dest, strprintf("legacy_%d", j), /*purpose=*/std::nullopt)); + CMutableTransaction mtx; - mtx.vout.emplace_back(COIN, GetScriptForDestination(*Assert(wallet->GetNewDestination(OutputType::BECH32, strprintf("bench_%d", j))))); - mtx.vout.emplace_back(COIN, GetScriptForDestination(*Assert(wallet->GetNewDestination(OutputType::LEGACY, strprintf("legacy_%d", j))))); + mtx.vout.emplace_back(COIN, GetScriptForDestination(dest)); mtx.vout.emplace_back(COIN, scripts_watch_only.at(j % NUM_WATCH_ONLY_ADDR)); mtx.vin.resize(2); wallet->AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}, /*update_wtx=*/nullptr, /*fFlushOnClose=*/false, /*rescanning_old_block=*/true); } - // Unload so the migration process loads it - TestUnloadWallet(std::move(wallet)); - - bench.epochs(/*numEpochs=*/1).run([&] { - util::Result res = MigrateLegacyToDescriptor(fs::PathToString(test_setup->m_path_root / "legacy"), "", context); + bench.epochs(/*numEpochs=*/1).run([&context, &wallet] { + util::Result res = MigrateLegacyToDescriptor(std::move(wallet), /*passphrase=*/"", context, /*was_loaded=*/false); assert(res); assert(res->wallet); assert(res->watchonly_wallet); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ea59f1a9d57..dd9dfcd516a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4398,9 +4398,8 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context) { - MigrationResult res; - bilingual_str error; std::vector warnings; + bilingual_str error; // If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it bool was_loaded = false; @@ -4452,10 +4451,23 @@ util::Result MigrateLegacyToDescriptor(const std::string& walle return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error}; } + return MigrateLegacyToDescriptor(std::move(local_wallet), passphrase, context, was_loaded); +} + +util::Result MigrateLegacyToDescriptor(std::shared_ptr local_wallet, const SecureString& passphrase, WalletContext& context, bool was_loaded) +{ + MigrationResult res; + bilingual_str error; + std::vector warnings; + + DatabaseOptions options; + options.require_existing = true; + DatabaseStatus status; + + const std::string wallet_name = local_wallet->GetName(); + // Helper to reload as normal for some of our exit scenarios const auto& reload_wallet = [&](std::shared_ptr& to_reload) { - // Reset options.require_format as wallets of any format may be reloaded. - options.require_format = std::nullopt; assert(to_reload.use_count() == 1); std::string name = to_reload->GetName(); to_reload.reset(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f8b914dd8ff..0afe71a40ef 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1135,6 +1135,8 @@ struct MigrationResult { //! Do all steps to migrate a legacy wallet to a descriptor wallet [[nodiscard]] util::Result 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. +[[nodiscard]] util::Result MigrateLegacyToDescriptor(std::shared_ptr local_wallet, const SecureString& passphrase, WalletContext& context, bool was_loaded); } // namespace wallet #endif // BITCOIN_WALLET_WALLET_H