Merge bitcoin/bitcoin#34358: wallet: fix removeprunedfunds bug with conflicting transactions

1f60ca360e wallet: fix removeprunedfunds bug with conflicting transactions (Martin Zumsande)

Pull request description:

  `removeprunedfunds` removes all entries from `mapTxSpends` for the inputs of the pruned tx. However, this is incorrect, because there could be multiple entries from conflicting transactions (that shouldn't be removed as well). This could lead to the wallet creating invalid transactions, trying to double spend utxos.
  The bug persists when the conflicting tx was mined, because the wallet trusts its internal accounting instead of calling `AddToSpends` again.

  The added test should fail on master.

ACKs for top commit:
  achow101:
    ACK 1f60ca360e
  fjahr:
    tACK 1f60ca360e
  furszy:
    utACK 1f60ca360e
  vasild:
    ACK 1f60ca360e

Tree-SHA512: 3cc9ed547530fd53e25721177b76ab2e1eae16ce2c0e63fc01b20fdbf8bd02655dae51167ad56f9dec748d34c61ce65d38f993370820601f8257c73b876a3347
This commit is contained in:
Ava Chow
2026-01-28 11:25:28 -08:00
2 changed files with 37 additions and 2 deletions

View File

@@ -2413,8 +2413,15 @@ util::Result<void> CWallet::RemoveTxs(WalletBatch& batch, std::vector<Txid>& txs
for (const auto& it : erased_txs) {
const Txid hash{it->first};
wtxOrdered.erase(it->second.m_it_wtxOrdered);
for (const auto& txin : it->second.tx->vin)
mapTxSpends.erase(txin.prevout);
for (const auto& txin : it->second.tx->vin) {
auto range = mapTxSpends.equal_range(txin.prevout);
for (auto iter = range.first; iter != range.second; ++iter) {
if (iter->second == hash) {
mapTxSpends.erase(iter);
break;
}
}
}
for (unsigned int i = 0; i < it->second.tx->vout.size(); ++i) {
m_txos.erase(COutPoint(hash, i));
}