@@ -752,8 +752,8 @@ bool CWallet::IsSpent(const COutPoint& outpoint) const
752752 const uint256& wtxid = it->second ;
753753 const auto mit = mapWallet.find (wtxid);
754754 if (mit != mapWallet.end ()) {
755- int depth = GetTxDepthInMainChain ( mit->second ) ;
756- if (depth > 0 || (depth == 0 && !mit-> second . isAbandoned () ))
755+ const auto & wtx = mit->second ;
756+ if (!wtx. isAbandoned () && !wtx. isBlockConflicted () && !wtx. isMempoolConflicted ( ))
757757 return true ; // Spent
758758 }
759759 }
@@ -1197,7 +1197,7 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
11971197 auto it = mapWallet.find (txin.prevout .hash );
11981198 if (it != mapWallet.end ()) {
11991199 CWalletTx& prevtx = it->second ;
1200- if (auto * prev = prevtx.state <TxStateConflicted >()) {
1200+ if (auto * prev = prevtx.state <TxStateBlockConflicted >()) {
12011201 MarkConflicted (prev->conflicting_block_hash , prev->conflicting_block_height , wtx.GetHash ());
12021202 }
12031203 }
@@ -1309,7 +1309,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
13091309 assert (!wtx.isConfirmed ());
13101310 assert (!wtx.InMempool ());
13111311 // If already conflicted or abandoned, no need to set abandoned
1312- if (!wtx.isConflicted () && !wtx.isAbandoned ()) {
1312+ if (!wtx.isBlockConflicted () && !wtx.isAbandoned ()) {
13131313 wtx.m_state = TxStateInactive{/* abandoned=*/ true };
13141314 return TxUpdate::NOTIFY_CHANGED;
13151315 }
@@ -1346,7 +1346,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
13461346 if (conflictconfirms < GetTxDepthInMainChain (wtx)) {
13471347 // Block is 'more conflicted' than current confirm; update.
13481348 // Mark transaction as conflicted with this block.
1349- wtx.m_state = TxStateConflicted {hashBlock, conflicting_height};
1349+ wtx.m_state = TxStateBlockConflicted {hashBlock, conflicting_height};
13501350 return TxUpdate::CHANGED;
13511351 }
13521352 return TxUpdate::UNCHANGED;
@@ -1360,7 +1360,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
13601360void CWallet::RecursiveUpdateTxState (const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
13611361 // Do not flush the wallet here for performance reasons
13621362 WalletBatch batch (GetDatabase (), false );
1363+ RecursiveUpdateTxState (&batch, tx_hash, try_updating_state);
1364+ }
13631365
1366+ void CWallet::RecursiveUpdateTxState (WalletBatch* batch, const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
13641367 std::set<uint256> todo;
13651368 std::set<uint256> done;
13661369
@@ -1377,7 +1380,7 @@ void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingSt
13771380 TxUpdate update_state = try_updating_state (wtx);
13781381 if (update_state != TxUpdate::UNCHANGED) {
13791382 wtx.MarkDirty ();
1380- batch. WriteTx (wtx);
1383+ if ( batch) batch-> WriteTx (wtx);
13811384 // Iterate over all its outputs, and update those tx states as well (if applicable)
13821385 for (unsigned int i = 0 ; i < wtx.tx ->vout .size (); ++i) {
13831386 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range (COutPoint (Txid::FromUint256 (now), i));
@@ -1418,6 +1421,20 @@ void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
14181421 if (it != mapWallet.end ()) {
14191422 RefreshMempoolStatus (it->second , chain ());
14201423 }
1424+
1425+ const Txid& txid = tx->GetHash ();
1426+
1427+ for (const CTxIn& tx_in : tx->vin ) {
1428+ // For each wallet transaction spending this prevout..
1429+ for (auto range = mapTxSpends.equal_range (tx_in.prevout ); range.first != range.second ; range.first ++) {
1430+ const uint256& spent_id = range.first ->second ;
1431+ // Skip the recently added tx
1432+ if (spent_id == txid) continue ;
1433+ RecursiveUpdateTxState (/* batch=*/ nullptr , spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED (cs_wallet) {
1434+ return wtx.mempool_conflicts .insert (txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED;
1435+ });
1436+ }
1437+ }
14211438}
14221439
14231440void CWallet::transactionRemovedFromMempool (const CTransactionRef& tx, MemPoolRemovalReason reason) {
@@ -1455,6 +1472,21 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
14551472 // https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
14561473 SyncTransaction (tx, TxStateInactive{});
14571474 }
1475+
1476+ const Txid& txid = tx->GetHash ();
1477+
1478+ for (const CTxIn& tx_in : tx->vin ) {
1479+ // Iterate over all wallet transactions spending txin.prev
1480+ // and recursively mark them as no longer conflicting with
1481+ // txid
1482+ for (auto range = mapTxSpends.equal_range (tx_in.prevout ); range.first != range.second ; range.first ++) {
1483+ const uint256& spent_id = range.first ->second ;
1484+
1485+ RecursiveUpdateTxState (/* batch=*/ nullptr , spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED (cs_wallet) {
1486+ return wtx.mempool_conflicts .erase (txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED;
1487+ });
1488+ }
1489+ }
14581490}
14591491
14601492void CWallet::blockConnected (ChainstateRole role, const interfaces::BlockInfo& block)
@@ -1506,11 +1538,11 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
15061538 for (TxSpends::const_iterator _it = range.first ; _it != range.second ; ++_it) {
15071539 CWalletTx& wtx = mapWallet.find (_it->second )->second ;
15081540
1509- if (!wtx.isConflicted ()) continue ;
1541+ if (!wtx.isBlockConflicted ()) continue ;
15101542
15111543 auto try_updating_state = [&](CWalletTx& tx) {
1512- if (!tx.isConflicted ()) return TxUpdate::UNCHANGED;
1513- if (tx.state <TxStateConflicted >()->conflicting_block_height >= disconnect_height) {
1544+ if (!tx.isBlockConflicted ()) return TxUpdate::UNCHANGED;
1545+ if (tx.state <TxStateBlockConflicted >()->conflicting_block_height >= disconnect_height) {
15141546 tx.m_state = TxStateInactive{};
15151547 return TxUpdate::CHANGED;
15161548 }
@@ -2787,7 +2819,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old
27872819 std::optional<uint256> block_hash;
27882820 if (auto * conf = wtx.state <TxStateConfirmed>()) {
27892821 block_hash = conf->confirmed_block_hash ;
2790- } else if (auto * conf = wtx.state <TxStateConflicted >()) {
2822+ } else if (auto * conf = wtx.state <TxStateBlockConflicted >()) {
27912823 block_hash = conf->conflicting_block_hash ;
27922824 }
27932825
@@ -3377,7 +3409,7 @@ int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
33773409 if (auto * conf = wtx.state <TxStateConfirmed>()) {
33783410 assert (conf->confirmed_block_height >= 0 );
33793411 return GetLastBlockHeight () - conf->confirmed_block_height + 1 ;
3380- } else if (auto * conf = wtx.state <TxStateConflicted >()) {
3412+ } else if (auto * conf = wtx.state <TxStateBlockConflicted >()) {
33813413 assert (conf->conflicting_block_height >= 0 );
33823414 return -1 * (GetLastBlockHeight () - conf->conflicting_block_height + 1 );
33833415 } else {
0 commit comments