@@ -3927,6 +3927,13 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39273927 }
39283928 }
39293929
3930+ // Get best block locator so that we can copy it to the watchonly and solvables
3931+ CBlockLocator best_block_locator;
3932+ if (!WalletBatch (GetDatabase ()).ReadBestBlock (best_block_locator)) {
3933+ error = _ (" Error: Unable to read wallet's best block locator record" );
3934+ return false ;
3935+ }
3936+
39303937 // Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.
39313938 // We need to go through these in the tx insertion order so that lookups to spends works.
39323939 std::vector<uint256> txids_to_delete;
@@ -3937,32 +3944,47 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39373944 LOCK (data.watchonly_wallet ->cs_wallet );
39383945 data.watchonly_wallet ->nOrderPosNext = nOrderPosNext;
39393946 watchonly_batch->WriteOrderPosNext (data.watchonly_wallet ->nOrderPosNext );
3947+ // Write the best block locator to avoid rescanning on reload
3948+ if (!watchonly_batch->WriteBestBlock (best_block_locator)) {
3949+ error = _ (" Error: Unable to write watchonly wallet best block locator record" );
3950+ return false ;
3951+ }
3952+ }
3953+ if (data.solvable_wallet ) {
3954+ // Write the best block locator to avoid rescanning on reload
3955+ if (!WalletBatch (data.solvable_wallet ->GetDatabase ()).WriteBestBlock (best_block_locator)) {
3956+ error = _ (" Error: Unable to write solvable wallet best block locator record" );
3957+ return false ;
3958+ }
39403959 }
39413960 for (const auto & [_pos, wtx] : wtxOrdered) {
3942- if (!IsMine (*wtx->tx ) && !IsFromMe (*wtx->tx )) {
3943- // Check it is the watchonly wallet's
3944- // solvable_wallet doesn't need to be checked because transactions for those scripts weren't being watched for
3945- if (data.watchonly_wallet ) {
3946- LOCK (data.watchonly_wallet ->cs_wallet );
3947- if (data.watchonly_wallet ->IsMine (*wtx->tx ) || data.watchonly_wallet ->IsFromMe (*wtx->tx )) {
3948- // Add to watchonly wallet
3949- const uint256& hash = wtx->GetHash ();
3950- const CWalletTx& to_copy_wtx = *wtx;
3951- if (!data.watchonly_wallet ->LoadToWallet (hash, [&](CWalletTx& ins_wtx, bool new_tx) EXCLUSIVE_LOCKS_REQUIRED (data.watchonly_wallet ->cs_wallet ) {
3952- if (!new_tx) return false ;
3953- ins_wtx.SetTx (to_copy_wtx.tx );
3954- ins_wtx.CopyFrom (to_copy_wtx);
3955- return true ;
3956- })) {
3957- error = strprintf (_ (" Error: Could not add watchonly tx %s to watchonly wallet" ), wtx->GetHash ().GetHex ());
3958- return false ;
3959- }
3960- watchonly_batch->WriteTx (data.watchonly_wallet ->mapWallet .at (hash));
3961- // Mark as to remove from this wallet
3961+ // Check it is the watchonly wallet's
3962+ // solvable_wallet doesn't need to be checked because transactions for those scripts weren't being watched for
3963+ bool is_mine = IsMine (*wtx->tx ) || IsFromMe (*wtx->tx );
3964+ if (data.watchonly_wallet ) {
3965+ LOCK (data.watchonly_wallet ->cs_wallet );
3966+ if (data.watchonly_wallet ->IsMine (*wtx->tx ) || data.watchonly_wallet ->IsFromMe (*wtx->tx )) {
3967+ // Add to watchonly wallet
3968+ const uint256& hash = wtx->GetHash ();
3969+ const CWalletTx& to_copy_wtx = *wtx;
3970+ if (!data.watchonly_wallet ->LoadToWallet (hash, [&](CWalletTx& ins_wtx, bool new_tx) EXCLUSIVE_LOCKS_REQUIRED (data.watchonly_wallet ->cs_wallet ) {
3971+ if (!new_tx) return false ;
3972+ ins_wtx.SetTx (to_copy_wtx.tx );
3973+ ins_wtx.CopyFrom (to_copy_wtx);
3974+ return true ;
3975+ })) {
3976+ error = strprintf (_ (" Error: Could not add watchonly tx %s to watchonly wallet" ), wtx->GetHash ().GetHex ());
3977+ return false ;
3978+ }
3979+ watchonly_batch->WriteTx (data.watchonly_wallet ->mapWallet .at (hash));
3980+ // Mark as to remove from the migrated wallet only if it does not also belong to it
3981+ if (!is_mine) {
39623982 txids_to_delete.push_back (hash);
3963- continue ;
39643983 }
3984+ continue ;
39653985 }
3986+ }
3987+ if (!is_mine) {
39663988 // Both not ours and not in the watchonly wallet
39673989 error = strprintf (_ (" Error: Transaction %s in wallet cannot be identified to belong to migrated wallets" ), wtx->GetHash ().GetHex ());
39683990 return false ;
@@ -4194,11 +4216,13 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
41944216 std::vector<bilingual_str> warnings;
41954217
41964218 // If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it
4219+ bool was_loaded = false ;
41974220 if (auto wallet = GetWallet (context, wallet_name)) {
41984221 if (!RemoveWallet (context, wallet, /* load_on_start=*/ std::nullopt , warnings)) {
41994222 return util::Error{_ (" Unable to unload the wallet before migrating" )};
42004223 }
42014224 UnloadWallet (std::move (wallet));
4225+ was_loaded = true ;
42024226 }
42034227
42044228 // Load the wallet but only in the context of this function.
@@ -4219,8 +4243,20 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
42194243 return util::Error{Untranslated (" Wallet loading failed." ) + Untranslated (" " ) + error};
42204244 }
42214245
4246+ // Helper to reload as normal for some of our exit scenarios
4247+ const auto & reload_wallet = [&](std::shared_ptr<CWallet>& to_reload) {
4248+ assert (to_reload.use_count () == 1 );
4249+ std::string name = to_reload->GetName ();
4250+ to_reload.reset ();
4251+ to_reload = LoadWallet (context, name, /* load_on_start=*/ std::nullopt , options, status, error, warnings);
4252+ return to_reload != nullptr ;
4253+ };
4254+
42224255 // Before anything else, check if there is something to migrate.
42234256 if (local_wallet->IsWalletFlagSet (WALLET_FLAG_DESCRIPTORS)) {
4257+ if (was_loaded) {
4258+ reload_wallet (local_wallet);
4259+ }
42244260 return util::Error{_ (" Error: This wallet is already a descriptor wallet" )};
42254261 }
42264262
@@ -4229,27 +4265,33 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
42294265 fs::path backup_filename = fs::PathFromString (strprintf (" %s-%d.legacy.bak" , wallet_name, GetTime ()));
42304266 fs::path backup_path = this_wallet_dir / backup_filename;
42314267 if (!local_wallet->BackupWallet (fs::PathToString (backup_path))) {
4268+ if (was_loaded) {
4269+ reload_wallet (local_wallet);
4270+ }
42324271 return util::Error{_ (" Error: Unable to make a backup of your wallet" )};
42334272 }
42344273 res.backup_path = backup_path;
42354274
42364275 bool success = false ;
4237- {
4238- LOCK (local_wallet->cs_wallet );
42394276
4240- // Unlock the wallet if needed
4241- if (local_wallet->IsLocked () && !local_wallet->Unlock (passphrase)) {
4242- if (passphrase.find (' \0 ' ) == std::string::npos) {
4243- return util::Error{Untranslated (" Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect." )};
4244- } else {
4245- return util::Error{Untranslated (" Error: Wallet decryption failed, the wallet passphrase entered was incorrect. "
4246- " The passphrase contains a null character (ie - a zero byte). "
4247- " If this passphrase was set with a version of this software prior to 25.0, "
4248- " please try again with only the characters up to — but not including — "
4249- " the first null character." )};
4250- }
4277+ // Unlock the wallet if needed
4278+ if (local_wallet->IsLocked () && !local_wallet->Unlock (passphrase)) {
4279+ if (was_loaded) {
4280+ reload_wallet (local_wallet);
4281+ }
4282+ if (passphrase.find (' \0 ' ) == std::string::npos) {
4283+ return util::Error{Untranslated (" Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect." )};
4284+ } else {
4285+ return util::Error{Untranslated (" Error: Wallet decryption failed, the wallet passphrase entered was incorrect. "
4286+ " The passphrase contains a null character (ie - a zero byte). "
4287+ " If this passphrase was set with a version of this software prior to 25.0, "
4288+ " please try again with only the characters up to — but not including — "
4289+ " the first null character." )};
42514290 }
4291+ }
42524292
4293+ {
4294+ LOCK (local_wallet->cs_wallet );
42534295 // First change to using SQLite
42544296 if (!local_wallet->MigrateToSQLite (error)) return util::Error{error};
42554297
@@ -4270,24 +4312,19 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
42704312 std::set<fs::path> wallet_dirs;
42714313 if (success) {
42724314 // Migration successful, unload all wallets locally, then reload them.
4273- const auto & reload_wallet = [&](std::shared_ptr<CWallet>& to_reload) {
4274- assert (to_reload.use_count () == 1 );
4275- std::string name = to_reload->GetName ();
4276- wallet_dirs.insert (fs::PathFromString (to_reload->GetDatabase ().Filename ()).parent_path ());
4277- to_reload.reset ();
4278- to_reload = LoadWallet (context, name, /* load_on_start=*/ std::nullopt , options, status, error, warnings);
4279- return to_reload != nullptr ;
4280- };
42814315 // Reload the main wallet
4316+ wallet_dirs.insert (fs::PathFromString (local_wallet->GetDatabase ().Filename ()).parent_path ());
42824317 success = reload_wallet (local_wallet);
42834318 res.wallet = local_wallet;
42844319 res.wallet_name = wallet_name;
42854320 if (success && res.watchonly_wallet ) {
42864321 // Reload watchonly
4322+ wallet_dirs.insert (fs::PathFromString (res.watchonly_wallet ->GetDatabase ().Filename ()).parent_path ());
42874323 success = reload_wallet (res.watchonly_wallet );
42884324 }
42894325 if (success && res.solvables_wallet ) {
42904326 // Reload solvables
4327+ wallet_dirs.insert (fs::PathFromString (res.solvables_wallet ->GetDatabase ().Filename ()).parent_path ());
42914328 success = reload_wallet (res.solvables_wallet );
42924329 }
42934330 }
0 commit comments