@@ -1266,11 +1266,6 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
1266
1266
{
1267
1267
LOCK (cs_wallet);
1268
1268
1269
- WalletBatch batch (GetDatabase ());
1270
-
1271
- std::set<uint256> todo;
1272
- std::set<uint256> done;
1273
-
1274
1269
// Can't mark abandoned if confirmed or in mempool
1275
1270
auto it = mapWallet.find (hashTx);
1276
1271
assert (it != mapWallet.end ());
@@ -1279,44 +1274,25 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
1279
1274
return false ;
1280
1275
}
1281
1276
1282
- todo.insert (hashTx);
1283
-
1284
- while (!todo.empty ()) {
1285
- uint256 now = *todo.begin ();
1286
- todo.erase (now);
1287
- done.insert (now);
1288
- auto it = mapWallet.find (now);
1289
- assert (it != mapWallet.end ());
1290
- CWalletTx& wtx = it->second ;
1291
- int currentconfirm = GetTxDepthInMainChain (wtx);
1292
- // If the orig tx was not in block, none of its spends can be
1293
- assert (currentconfirm <= 0 );
1294
- // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
1295
- if (currentconfirm == 0 && !wtx.isAbandoned ()) {
1296
- // If the orig tx was not in block/mempool, none of its spends can be in mempool
1297
- assert (!wtx.InMempool ());
1277
+ auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED (cs_wallet) {
1278
+ // If the orig tx was not in block/mempool, none of its spends can be.
1279
+ assert (!wtx.isConfirmed ());
1280
+ assert (!wtx.InMempool ());
1281
+ // If already conflicted or abandoned, no need to set abandoned
1282
+ if (!wtx.isConflicted () && !wtx.isAbandoned ()) {
1298
1283
wtx.m_state = TxStateInactive{/* abandoned=*/ true };
1299
- wtx.MarkDirty ();
1300
- batch.WriteTx (wtx);
1301
- NotifyTransactionChanged (wtx.GetHash (), CT_UPDATED);
1302
- // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
1303
- // States are not permanent, so these transactions can become unabandoned if they are re-added to the
1304
- // mempool, or confirmed in a block, or conflicted.
1305
- // Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
1306
- // states change will remain abandoned and will require manual broadcast if the user wants them.
1307
- for (unsigned int i = 0 ; i < wtx.tx ->vout .size (); ++i) {
1308
- std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range (COutPoint (now, i));
1309
- for (TxSpends::const_iterator iter = range.first ; iter != range.second ; ++iter) {
1310
- if (!done.count (iter->second )) {
1311
- todo.insert (iter->second );
1312
- }
1313
- }
1314
- }
1315
- // If a transaction changes 'conflicted' state, that changes the balance
1316
- // available of the outputs it spends. So force those to be recomputed
1317
- MarkInputsDirty (wtx.tx );
1284
+ return TxUpdate::NOTIFY_CHANGED;
1318
1285
}
1319
- }
1286
+ return TxUpdate::UNCHANGED;
1287
+ };
1288
+
1289
+ // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
1290
+ // States are not permanent, so these transactions can become unabandoned if they are re-added to the
1291
+ // mempool, or confirmed in a block, or conflicted.
1292
+ // Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
1293
+ // states change will remain abandoned and will require manual broadcast if the user wants them.
1294
+
1295
+ RecursiveUpdateTxState (hashTx, try_updating_state);
1320
1296
1321
1297
return true ;
1322
1298
}
@@ -1333,13 +1309,29 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
1333
1309
if (conflictconfirms >= 0 )
1334
1310
return ;
1335
1311
1312
+ auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED (cs_wallet) {
1313
+ if (conflictconfirms < GetTxDepthInMainChain (wtx)) {
1314
+ // Block is 'more conflicted' than current confirm; update.
1315
+ // Mark transaction as conflicted with this block.
1316
+ wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
1317
+ return TxUpdate::CHANGED;
1318
+ }
1319
+ return TxUpdate::UNCHANGED;
1320
+ };
1321
+
1322
+ // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too.
1323
+ RecursiveUpdateTxState (hashTx, try_updating_state);
1324
+
1325
+ }
1326
+
1327
+ void CWallet::RecursiveUpdateTxState (const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
1336
1328
// Do not flush the wallet here for performance reasons
1337
1329
WalletBatch batch (GetDatabase (), false );
1338
1330
1339
1331
std::set<uint256> todo;
1340
1332
std::set<uint256> done;
1341
1333
1342
- todo.insert (hashTx );
1334
+ todo.insert (tx_hash );
1343
1335
1344
1336
while (!todo.empty ()) {
1345
1337
uint256 now = *todo.begin ();
@@ -1348,14 +1340,12 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
1348
1340
auto it = mapWallet.find (now);
1349
1341
assert (it != mapWallet.end ());
1350
1342
CWalletTx& wtx = it->second ;
1351
- int currentconfirm = GetTxDepthInMainChain (wtx);
1352
- if (conflictconfirms < currentconfirm) {
1353
- // Block is 'more conflicted' than current confirm; update.
1354
- // Mark transaction as conflicted with this block.
1355
- wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
1343
+
1344
+ TxUpdate update_state = try_updating_state (wtx);
1345
+ if (update_state != TxUpdate::UNCHANGED) {
1356
1346
wtx.MarkDirty ();
1357
1347
batch.WriteTx (wtx);
1358
- // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
1348
+ // Iterate over all its outputs, and update those tx states as well (if applicable)
1359
1349
for (unsigned int i = 0 ; i < wtx.tx ->vout .size (); ++i) {
1360
1350
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range (COutPoint (now, i));
1361
1351
for (TxSpends::const_iterator iter = range.first ; iter != range.second ; ++iter) {
@@ -1364,7 +1354,12 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
1364
1354
}
1365
1355
}
1366
1356
}
1367
- // If a transaction changes 'conflicted' state, that changes the balance
1357
+
1358
+ if (update_state == TxUpdate::NOTIFY_CHANGED) {
1359
+ NotifyTransactionChanged (wtx.GetHash (), CT_UPDATED);
1360
+ }
1361
+
1362
+ // If a transaction changes its tx state, that usually changes the balance
1368
1363
// available of the outputs it spends. So force those to be recomputed
1369
1364
MarkInputsDirty (wtx.tx );
1370
1365
}
@@ -1459,8 +1454,36 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
1459
1454
// future with a stickier abandoned state or even removing abandontransaction call.
1460
1455
m_last_block_processed_height = block.height - 1 ;
1461
1456
m_last_block_processed = *Assert (block.prev_hash );
1457
+
1458
+ int disconnect_height = block.height ;
1459
+
1462
1460
for (const CTransactionRef& ptx : Assert (block.data )->vtx ) {
1463
1461
SyncTransaction (ptx, TxStateInactive{});
1462
+
1463
+ for (const CTxIn& tx_in : ptx->vin ) {
1464
+ // No other wallet transactions conflicted with this transaction
1465
+ if (mapTxSpends.count (tx_in.prevout ) < 1 ) continue ;
1466
+
1467
+ std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range (tx_in.prevout );
1468
+
1469
+ // For all of the spends that conflict with this transaction
1470
+ for (TxSpends::const_iterator _it = range.first ; _it != range.second ; ++_it) {
1471
+ CWalletTx& wtx = mapWallet.find (_it->second )->second ;
1472
+
1473
+ if (!wtx.isConflicted ()) continue ;
1474
+
1475
+ auto try_updating_state = [&](CWalletTx& tx) {
1476
+ if (!tx.isConflicted ()) return TxUpdate::UNCHANGED;
1477
+ if (tx.state <TxStateConflicted>()->conflicting_block_height >= disconnect_height) {
1478
+ tx.m_state = TxStateInactive{};
1479
+ return TxUpdate::CHANGED;
1480
+ }
1481
+ return TxUpdate::UNCHANGED;
1482
+ };
1483
+
1484
+ RecursiveUpdateTxState (wtx.tx ->GetHash (), try_updating_state);
1485
+ }
1486
+ }
1464
1487
}
1465
1488
}
1466
1489
0 commit comments