@@ -752,8 +752,8 @@ bool CWallet::IsSpent(const COutPoint& outpoint) const
752
752
const uint256& wtxid = it->second ;
753
753
const auto mit = mapWallet.find (wtxid);
754
754
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 ( ))
757
757
return true ; // Spent
758
758
}
759
759
}
@@ -1197,7 +1197,7 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
1197
1197
auto it = mapWallet.find (txin.prevout .hash );
1198
1198
if (it != mapWallet.end ()) {
1199
1199
CWalletTx& prevtx = it->second ;
1200
- if (auto * prev = prevtx.state <TxStateConflicted >()) {
1200
+ if (auto * prev = prevtx.state <TxStateBlockConflicted >()) {
1201
1201
MarkConflicted (prev->conflicting_block_hash , prev->conflicting_block_height , wtx.GetHash ());
1202
1202
}
1203
1203
}
@@ -1309,7 +1309,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
1309
1309
assert (!wtx.isConfirmed ());
1310
1310
assert (!wtx.InMempool ());
1311
1311
// If already conflicted or abandoned, no need to set abandoned
1312
- if (!wtx.isConflicted () && !wtx.isAbandoned ()) {
1312
+ if (!wtx.isBlockConflicted () && !wtx.isAbandoned ()) {
1313
1313
wtx.m_state = TxStateInactive{/* abandoned=*/ true };
1314
1314
return TxUpdate::NOTIFY_CHANGED;
1315
1315
}
@@ -1346,7 +1346,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
1346
1346
if (conflictconfirms < GetTxDepthInMainChain (wtx)) {
1347
1347
// Block is 'more conflicted' than current confirm; update.
1348
1348
// Mark transaction as conflicted with this block.
1349
- wtx.m_state = TxStateConflicted {hashBlock, conflicting_height};
1349
+ wtx.m_state = TxStateBlockConflicted {hashBlock, conflicting_height};
1350
1350
return TxUpdate::CHANGED;
1351
1351
}
1352
1352
return TxUpdate::UNCHANGED;
@@ -1360,7 +1360,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
1360
1360
void CWallet::RecursiveUpdateTxState (const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
1361
1361
// Do not flush the wallet here for performance reasons
1362
1362
WalletBatch batch (GetDatabase (), false );
1363
+ RecursiveUpdateTxState (&batch, tx_hash, try_updating_state);
1364
+ }
1363
1365
1366
+ void CWallet::RecursiveUpdateTxState (WalletBatch* batch, const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
1364
1367
std::set<uint256> todo;
1365
1368
std::set<uint256> done;
1366
1369
@@ -1377,7 +1380,7 @@ void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingSt
1377
1380
TxUpdate update_state = try_updating_state (wtx);
1378
1381
if (update_state != TxUpdate::UNCHANGED) {
1379
1382
wtx.MarkDirty ();
1380
- batch. WriteTx (wtx);
1383
+ if ( batch) batch-> WriteTx (wtx);
1381
1384
// Iterate over all its outputs, and update those tx states as well (if applicable)
1382
1385
for (unsigned int i = 0 ; i < wtx.tx ->vout .size (); ++i) {
1383
1386
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) {
1418
1421
if (it != mapWallet.end ()) {
1419
1422
RefreshMempoolStatus (it->second , chain ());
1420
1423
}
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
+ }
1421
1438
}
1422
1439
1423
1440
void CWallet::transactionRemovedFromMempool (const CTransactionRef& tx, MemPoolRemovalReason reason) {
@@ -1455,6 +1472,21 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
1455
1472
// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
1456
1473
SyncTransaction (tx, TxStateInactive{});
1457
1474
}
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
+ }
1458
1490
}
1459
1491
1460
1492
void CWallet::blockConnected (ChainstateRole role, const interfaces::BlockInfo& block)
@@ -1506,11 +1538,11 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
1506
1538
for (TxSpends::const_iterator _it = range.first ; _it != range.second ; ++_it) {
1507
1539
CWalletTx& wtx = mapWallet.find (_it->second )->second ;
1508
1540
1509
- if (!wtx.isConflicted ()) continue ;
1541
+ if (!wtx.isBlockConflicted ()) continue ;
1510
1542
1511
1543
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) {
1514
1546
tx.m_state = TxStateInactive{};
1515
1547
return TxUpdate::CHANGED;
1516
1548
}
@@ -2787,7 +2819,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old
2787
2819
std::optional<uint256> block_hash;
2788
2820
if (auto * conf = wtx.state <TxStateConfirmed>()) {
2789
2821
block_hash = conf->confirmed_block_hash ;
2790
- } else if (auto * conf = wtx.state <TxStateConflicted >()) {
2822
+ } else if (auto * conf = wtx.state <TxStateBlockConflicted >()) {
2791
2823
block_hash = conf->conflicting_block_hash ;
2792
2824
}
2793
2825
@@ -3377,7 +3409,7 @@ int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
3377
3409
if (auto * conf = wtx.state <TxStateConfirmed>()) {
3378
3410
assert (conf->confirmed_block_height >= 0 );
3379
3411
return GetLastBlockHeight () - conf->confirmed_block_height + 1 ;
3380
- } else if (auto * conf = wtx.state <TxStateConflicted >()) {
3412
+ } else if (auto * conf = wtx.state <TxStateBlockConflicted >()) {
3381
3413
assert (conf->conflicting_block_height >= 0 );
3382
3414
return -1 * (GetLastBlockHeight () - conf->conflicting_block_height + 1 );
3383
3415
} else {
0 commit comments