@@ -3908,6 +3908,14 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
3908
3908
// Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.
3909
3909
// We need to go through these in the tx insertion order so that lookups to spends works.
3910
3910
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
+ }
3911
3919
for (const auto & [_pos, wtx] : wtxOrdered) {
3912
3920
if (!IsMine (*wtx->tx ) && !IsFromMe (*wtx->tx )) {
3913
3921
// Check it is the watchonly wallet's
@@ -3916,12 +3924,20 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
3916
3924
LOCK (data.watchonly_wallet ->cs_wallet );
3917
3925
if (data.watchonly_wallet ->IsMine (*wtx->tx ) || data.watchonly_wallet ->IsFromMe (*wtx->tx )) {
3918
3926
// 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 ());
3921
3936
return false ;
3922
3937
}
3938
+ watchonly_batch->WriteTx (data.watchonly_wallet ->mapWallet .at (hash));
3923
3939
// Mark as to remove from this wallet
3924
- txids_to_delete.push_back (wtx-> GetHash () );
3940
+ txids_to_delete.push_back (hash );
3925
3941
continue ;
3926
3942
}
3927
3943
}
@@ -3930,6 +3946,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
3930
3946
return false ;
3931
3947
}
3932
3948
}
3949
+ watchonly_batch.reset (); // Flush
3933
3950
// Do the removes
3934
3951
if (txids_to_delete.size () > 0 ) {
3935
3952
std::vector<uint256> deleted_txids;
@@ -4066,6 +4083,10 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
4066
4083
DatabaseOptions options;
4067
4084
options.require_existing = false ;
4068
4085
options.require_create = true ;
4086
+ options.require_format = DatabaseFormat::SQLITE;
4087
+
4088
+ WalletContext empty_context;
4089
+ empty_context.args = context.args ;
4069
4090
4070
4091
// Make the wallets
4071
4092
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,
4081
4102
DatabaseStatus status;
4082
4103
std::vector<bilingual_str> warnings;
4083
4104
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 ) {
4086
4113
error = _ (" Error: Failed to create new watchonly wallet" );
4087
4114
return false ;
4088
4115
}
@@ -4112,8 +4139,14 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
4112
4139
DatabaseStatus status;
4113
4140
std::vector<bilingual_str> warnings;
4114
4141
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 ) {
4117
4150
error = _ (" Error: Failed to create new watchonly wallet" );
4118
4151
return false ;
4119
4152
}
@@ -4216,47 +4249,69 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
4216
4249
success = DoMigration (*local_wallet, context, error, res);
4217
4250
}
4218
4251
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;
4219
4257
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;
4224
4270
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) {
4226
4281
// Migration failed, cleanup
4227
4282
// Copy the backup to the actual wallet dir
4228
4283
fs::path temp_backup_location = fsbridge::AbsPathJoin (GetWalletDir (), backup_filename);
4229
4284
fs::copy_file (backup_path, temp_backup_location, fs::copy_options::none);
4230
4285
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
-
4239
4286
// Make list of wallets to cleanup
4240
4287
std::vector<std::shared_ptr<CWallet>> created_wallets;
4288
+ if (local_wallet) created_wallets.push_back (std::move (local_wallet));
4241
4289
if (res.watchonly_wallet ) created_wallets.push_back (std::move (res.watchonly_wallet ));
4242
4290
if (res.solvables_wallet ) created_wallets.push_back (std::move (res.solvables_wallet ));
4243
4291
4244
4292
// Get the directories to remove after unloading
4245
4293
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 ());
4247
4295
}
4248
4296
4249
4297
// Unload the wallets
4250
4298
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 ();
4254
4310
}
4255
- UnloadWallet (std::move (w));
4256
4311
}
4257
4312
4258
4313
// Delete the wallet directories
4259
- for (fs::path& dir : wallet_dirs) {
4314
+ for (const fs::path& dir : wallet_dirs) {
4260
4315
fs::remove_all (dir);
4261
4316
}
4262
4317
0 commit comments