@@ -3908,6 +3908,14 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39083908 // Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.
39093909 // We need to go through these in the tx insertion order so that lookups to spends works.
39103910 std::vector<uint256> txids_to_delete;
3911+ std::unique_ptr<WalletBatch> watchonly_batch;
3912+ if (data.watchonly_wallet ) {
3913+ watchonly_batch = std::make_unique<WalletBatch>(data.watchonly_wallet ->GetDatabase ());
3914+ // Copy the next tx order pos to the watchonly wallet
3915+ LOCK (data.watchonly_wallet ->cs_wallet );
3916+ data.watchonly_wallet ->nOrderPosNext = nOrderPosNext;
3917+ watchonly_batch->WriteOrderPosNext (data.watchonly_wallet ->nOrderPosNext );
3918+ }
39113919 for (const auto & [_pos, wtx] : wtxOrdered) {
39123920 if (!IsMine (*wtx->tx ) && !IsFromMe (*wtx->tx )) {
39133921 // Check it is the watchonly wallet's
@@ -3916,12 +3924,20 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39163924 LOCK (data.watchonly_wallet ->cs_wallet );
39173925 if (data.watchonly_wallet ->IsMine (*wtx->tx ) || data.watchonly_wallet ->IsFromMe (*wtx->tx )) {
39183926 // Add to watchonly wallet
3919- if (!data.watchonly_wallet ->AddToWallet (wtx->tx , wtx->m_state )) {
3920- error = _ (" Error: Could not add watchonly tx to watchonly wallet" );
3927+ const uint256& hash = wtx->GetHash ();
3928+ const CWalletTx& to_copy_wtx = *wtx;
3929+ if (!data.watchonly_wallet ->LoadToWallet (hash, [&](CWalletTx& ins_wtx, bool new_tx) EXCLUSIVE_LOCKS_REQUIRED (data.watchonly_wallet ->cs_wallet ) {
3930+ if (!new_tx) return false ;
3931+ ins_wtx.SetTx (to_copy_wtx.tx );
3932+ ins_wtx.CopyFrom (to_copy_wtx);
3933+ return true ;
3934+ })) {
3935+ error = strprintf (_ (" Error: Could not add watchonly tx %s to watchonly wallet" ), wtx->GetHash ().GetHex ());
39213936 return false ;
39223937 }
3938+ watchonly_batch->WriteTx (data.watchonly_wallet ->mapWallet .at (hash));
39233939 // Mark as to remove from this wallet
3924- txids_to_delete.push_back (wtx-> GetHash () );
3940+ txids_to_delete.push_back (hash );
39253941 continue ;
39263942 }
39273943 }
@@ -3930,6 +3946,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39303946 return false ;
39313947 }
39323948 }
3949+ watchonly_batch.reset (); // Flush
39333950 // Do the removes
39343951 if (txids_to_delete.size () > 0 ) {
39353952 std::vector<uint256> deleted_txids;
@@ -4066,6 +4083,10 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
40664083 DatabaseOptions options;
40674084 options.require_existing = false ;
40684085 options.require_create = true ;
4086+ options.require_format = DatabaseFormat::SQLITE;
4087+
4088+ WalletContext empty_context;
4089+ empty_context.args = context.args ;
40694090
40704091 // Make the wallets
40714092 options.create_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_DESCRIPTORS;
@@ -4081,8 +4102,14 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
40814102 DatabaseStatus status;
40824103 std::vector<bilingual_str> warnings;
40834104 std::string wallet_name = wallet.GetName () + " _watchonly" ;
4084- data->watchonly_wallet = CreateWallet (context, wallet_name, std::nullopt , options, status, error, warnings);
4085- if (status != DatabaseStatus::SUCCESS) {
4105+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase (wallet_name, options, status, error);
4106+ if (!database) {
4107+ error = strprintf (_ (" Wallet file creation failed: %s" ), error);
4108+ return false ;
4109+ }
4110+
4111+ data->watchonly_wallet = CWallet::Create (empty_context, wallet_name, std::move (database), options.create_flags , error, warnings);
4112+ if (!data->watchonly_wallet ) {
40864113 error = _ (" Error: Failed to create new watchonly wallet" );
40874114 return false ;
40884115 }
@@ -4112,8 +4139,14 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
41124139 DatabaseStatus status;
41134140 std::vector<bilingual_str> warnings;
41144141 std::string wallet_name = wallet.GetName () + " _solvables" ;
4115- data->solvable_wallet = CreateWallet (context, wallet_name, std::nullopt , options, status, error, warnings);
4116- if (status != DatabaseStatus::SUCCESS) {
4142+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase (wallet_name, options, status, error);
4143+ if (!database) {
4144+ error = strprintf (_ (" Wallet file creation failed: %s" ), error);
4145+ return false ;
4146+ }
4147+
4148+ data->solvable_wallet = CWallet::Create (empty_context, wallet_name, std::move (database), options.create_flags , error, warnings);
4149+ if (!data->solvable_wallet ) {
41174150 error = _ (" Error: Failed to create new watchonly wallet" );
41184151 return false ;
41194152 }
@@ -4216,47 +4249,69 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
42164249 success = DoMigration (*local_wallet, context, error, res);
42174250 }
42184251
4252+ // In case of reloading failure, we need to remember the wallet dirs to remove
4253+ // Set is used as it may be populated with the same wallet directory paths multiple times,
4254+ // both before and after reloading. This ensures the set is complete even if one of the wallets
4255+ // fails to reload.
4256+ std::set<fs::path> wallet_dirs;
42194257 if (success) {
4220- // Migration successful, unload the wallet locally, then reload it.
4221- assert (local_wallet.use_count () == 1 );
4222- local_wallet.reset ();
4223- res.wallet = LoadWallet (context, wallet_name, /* load_on_start=*/ std::nullopt , options, status, error, warnings);
4258+ // Migration successful, unload all wallets locally, then reload them.
4259+ const auto & reload_wallet = [&](std::shared_ptr<CWallet>& to_reload) {
4260+ assert (to_reload.use_count () == 1 );
4261+ std::string name = to_reload->GetName ();
4262+ wallet_dirs.insert (fs::PathFromString (to_reload->GetDatabase ().Filename ()).parent_path ());
4263+ to_reload.reset ();
4264+ to_reload = LoadWallet (context, name, /* load_on_start=*/ std::nullopt , options, status, error, warnings);
4265+ return to_reload != nullptr ;
4266+ };
4267+ // Reload the main wallet
4268+ success = reload_wallet (local_wallet);
4269+ res.wallet = local_wallet;
42244270 res.wallet_name = wallet_name;
4225- } else {
4271+ if (success && res.watchonly_wallet ) {
4272+ // Reload watchonly
4273+ success = reload_wallet (res.watchonly_wallet );
4274+ }
4275+ if (success && res.solvables_wallet ) {
4276+ // Reload solvables
4277+ success = reload_wallet (res.solvables_wallet );
4278+ }
4279+ }
4280+ if (!success) {
42264281 // Migration failed, cleanup
42274282 // Copy the backup to the actual wallet dir
42284283 fs::path temp_backup_location = fsbridge::AbsPathJoin (GetWalletDir (), backup_filename);
42294284 fs::copy_file (backup_path, temp_backup_location, fs::copy_options::none);
42304285
4231- // Remember this wallet's walletdir to remove after unloading
4232- std::vector<fs::path> wallet_dirs;
4233- wallet_dirs.emplace_back (fs::PathFromString (local_wallet->GetDatabase ().Filename ()).parent_path ());
4234-
4235- // Unload the wallet locally
4236- assert (local_wallet.use_count () == 1 );
4237- local_wallet.reset ();
4238-
42394286 // Make list of wallets to cleanup
42404287 std::vector<std::shared_ptr<CWallet>> created_wallets;
4288+ if (local_wallet) created_wallets.push_back (std::move (local_wallet));
42414289 if (res.watchonly_wallet ) created_wallets.push_back (std::move (res.watchonly_wallet ));
42424290 if (res.solvables_wallet ) created_wallets.push_back (std::move (res.solvables_wallet ));
42434291
42444292 // Get the directories to remove after unloading
42454293 for (std::shared_ptr<CWallet>& w : created_wallets) {
4246- wallet_dirs.emplace_back (fs::PathFromString (w->GetDatabase ().Filename ()).parent_path ());
4294+ wallet_dirs.emplace (fs::PathFromString (w->GetDatabase ().Filename ()).parent_path ());
42474295 }
42484296
42494297 // Unload the wallets
42504298 for (std::shared_ptr<CWallet>& w : created_wallets) {
4251- if (!RemoveWallet (context, w, /* load_on_start=*/ false )) {
4252- error += _ (" \n Unable to cleanup failed migration" );
4253- return util::Error{error};
4299+ if (w->HaveChain ()) {
4300+ // Unloading for wallets that were loaded for normal use
4301+ if (!RemoveWallet (context, w, /* load_on_start=*/ false )) {
4302+ error += _ (" \n Unable to cleanup failed migration" );
4303+ return util::Error{error};
4304+ }
4305+ UnloadWallet (std::move (w));
4306+ } else {
4307+ // Unloading for wallets in local context
4308+ assert (w.use_count () == 1 );
4309+ w.reset ();
42544310 }
4255- UnloadWallet (std::move (w));
42564311 }
42574312
42584313 // Delete the wallet directories
4259- for (fs::path& dir : wallet_dirs) {
4314+ for (const fs::path& dir : wallet_dirs) {
42604315 fs::remove_all (dir);
42614316 }
42624317
0 commit comments