diff --git a/src/wallet/transaction.cpp b/src/wallet/transaction.cpp index a46846c1d4a..4f78fe75205 100644 --- a/src/wallet/transaction.cpp +++ b/src/wallet/transaction.cpp @@ -24,4 +24,9 @@ int64_t CWalletTx::GetTxTime() const int64_t n = nTimeSmart; return n ? n : nTimeReceived; } + +void CWalletTx::CopyFrom(const CWalletTx& _tx) +{ + *this = _tx; +} } // namespace wallet diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h index 1a79d7db4ed..fa6c8d6f3b4 100644 --- a/src/wallet/transaction.h +++ b/src/wallet/transaction.h @@ -323,11 +323,15 @@ public: const uint256& GetWitnessHash() const { return tx->GetWitnessHash(); } bool IsCoinBase() const { return tx->IsCoinBase(); } +private: // Disable copying of CWalletTx objects to prevent bugs where instances get // copied in and out of the mapWallet map, and fields are updated in the // wrong copy. - CWalletTx(CWalletTx const &) = delete; - void operator=(CWalletTx const &x) = delete; + CWalletTx(const CWalletTx&) = default; + CWalletTx& operator=(const CWalletTx&) = default; +public: + // Instead have an explicit copy function + void CopyFrom(const CWalletTx&); }; struct WalletTxOrderComparator { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5d2f9e2fb69..5caa43edb42 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3908,6 +3908,14 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) // Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet. // We need to go through these in the tx insertion order so that lookups to spends works. std::vector txids_to_delete; + std::unique_ptr watchonly_batch; + if (data.watchonly_wallet) { + watchonly_batch = std::make_unique(data.watchonly_wallet->GetDatabase()); + // Copy the next tx order pos to the watchonly wallet + LOCK(data.watchonly_wallet->cs_wallet); + data.watchonly_wallet->nOrderPosNext = nOrderPosNext; + watchonly_batch->WriteOrderPosNext(data.watchonly_wallet->nOrderPosNext); + } for (const auto& [_pos, wtx] : wtxOrdered) { if (!IsMine(*wtx->tx) && !IsFromMe(*wtx->tx)) { // Check it is the watchonly wallet's @@ -3916,12 +3924,20 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) LOCK(data.watchonly_wallet->cs_wallet); if (data.watchonly_wallet->IsMine(*wtx->tx) || data.watchonly_wallet->IsFromMe(*wtx->tx)) { // Add to watchonly wallet - if (!data.watchonly_wallet->AddToWallet(wtx->tx, wtx->m_state)) { - error = _("Error: Could not add watchonly tx to watchonly wallet"); + const uint256& hash = wtx->GetHash(); + const CWalletTx& to_copy_wtx = *wtx; + if (!data.watchonly_wallet->LoadToWallet(hash, [&](CWalletTx& ins_wtx, bool new_tx) EXCLUSIVE_LOCKS_REQUIRED(data.watchonly_wallet->cs_wallet) { + if (!new_tx) return false; + ins_wtx.SetTx(to_copy_wtx.tx); + ins_wtx.CopyFrom(to_copy_wtx); + return true; + })) { + error = strprintf(_("Error: Could not add watchonly tx %s to watchonly wallet"), wtx->GetHash().GetHex()); return false; } + watchonly_batch->WriteTx(data.watchonly_wallet->mapWallet.at(hash)); // Mark as to remove from this wallet - txids_to_delete.push_back(wtx->GetHash()); + txids_to_delete.push_back(hash); continue; } } @@ -3930,6 +3946,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) return false; } } + watchonly_batch.reset(); // Flush // Do the removes if (txids_to_delete.size() > 0) { std::vector deleted_txids;