@@ -3927,6 +3927,13 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
3927
3927
}
3928
3928
}
3929
3929
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
+
3930
3937
// Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.
3931
3938
// We need to go through these in the tx insertion order so that lookups to spends works.
3932
3939
std::vector<uint256> txids_to_delete;
@@ -3937,32 +3944,47 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
3937
3944
LOCK (data.watchonly_wallet ->cs_wallet );
3938
3945
data.watchonly_wallet ->nOrderPosNext = nOrderPosNext;
3939
3946
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
+ }
3940
3959
}
3941
3960
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) {
3962
3982
txids_to_delete.push_back (hash);
3963
- continue ;
3964
3983
}
3984
+ continue ;
3965
3985
}
3986
+ }
3987
+ if (!is_mine) {
3966
3988
// Both not ours and not in the watchonly wallet
3967
3989
error = strprintf (_ (" Error: Transaction %s in wallet cannot be identified to belong to migrated wallets" ), wtx->GetHash ().GetHex ());
3968
3990
return false ;
@@ -4194,11 +4216,13 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
4194
4216
std::vector<bilingual_str> warnings;
4195
4217
4196
4218
// 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 ;
4197
4220
if (auto wallet = GetWallet (context, wallet_name)) {
4198
4221
if (!RemoveWallet (context, wallet, /* load_on_start=*/ std::nullopt, warnings)) {
4199
4222
return util::Error{_ (" Unable to unload the wallet before migrating" )};
4200
4223
}
4201
4224
UnloadWallet (std::move (wallet));
4225
+ was_loaded = true ;
4202
4226
}
4203
4227
4204
4228
// Load the wallet but only in the context of this function.
@@ -4219,8 +4243,20 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
4219
4243
return util::Error{Untranslated (" Wallet loading failed." ) + Untranslated (" " ) + error};
4220
4244
}
4221
4245
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
+
4222
4255
// Before anything else, check if there is something to migrate.
4223
4256
if (local_wallet->IsWalletFlagSet (WALLET_FLAG_DESCRIPTORS)) {
4257
+ if (was_loaded) {
4258
+ reload_wallet (local_wallet);
4259
+ }
4224
4260
return util::Error{_ (" Error: This wallet is already a descriptor wallet" )};
4225
4261
}
4226
4262
@@ -4229,27 +4265,33 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
4229
4265
fs::path backup_filename = fs::PathFromString (strprintf (" %s-%d.legacy.bak" , wallet_name, GetTime ()));
4230
4266
fs::path backup_path = this_wallet_dir / backup_filename;
4231
4267
if (!local_wallet->BackupWallet (fs::PathToString (backup_path))) {
4268
+ if (was_loaded) {
4269
+ reload_wallet (local_wallet);
4270
+ }
4232
4271
return util::Error{_ (" Error: Unable to make a backup of your wallet" )};
4233
4272
}
4234
4273
res.backup_path = backup_path;
4235
4274
4236
4275
bool success = false ;
4237
- {
4238
- LOCK (local_wallet->cs_wallet );
4239
4276
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." )};
4251
4290
}
4291
+ }
4252
4292
4293
+ {
4294
+ LOCK (local_wallet->cs_wallet );
4253
4295
// First change to using SQLite
4254
4296
if (!local_wallet->MigrateToSQLite (error)) return util::Error{error};
4255
4297
@@ -4270,24 +4312,19 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
4270
4312
std::set<fs::path> wallet_dirs;
4271
4313
if (success) {
4272
4314
// 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
- };
4281
4315
// Reload the main wallet
4316
+ wallet_dirs.insert (fs::PathFromString (local_wallet->GetDatabase ().Filename ()).parent_path ());
4282
4317
success = reload_wallet (local_wallet);
4283
4318
res.wallet = local_wallet;
4284
4319
res.wallet_name = wallet_name;
4285
4320
if (success && res.watchonly_wallet ) {
4286
4321
// Reload watchonly
4322
+ wallet_dirs.insert (fs::PathFromString (res.watchonly_wallet ->GetDatabase ().Filename ()).parent_path ());
4287
4323
success = reload_wallet (res.watchonly_wallet );
4288
4324
}
4289
4325
if (success && res.solvables_wallet ) {
4290
4326
// Reload solvables
4327
+ wallet_dirs.insert (fs::PathFromString (res.solvables_wallet ->GetDatabase ().Filename ()).parent_path ());
4291
4328
success = reload_wallet (res.solvables_wallet );
4292
4329
}
4293
4330
}
0 commit comments