Skip to content

Commit 096487c

Browse files
ishaanamariard
andcommitted
wallet: introduce generic recursive tx state updating function
This commit also changed `MarkConflicted` and `AbandonTransaction` to use this new function Co-authored-by: ariard <[email protected]>
1 parent 49d07ea commit 096487c

File tree

2 files changed

+51
-49
lines changed

2 files changed

+51
-49
lines changed

src/wallet/wallet.cpp

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,11 +1264,6 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
12641264
{
12651265
LOCK(cs_wallet);
12661266

1267-
WalletBatch batch(GetDatabase());
1268-
1269-
std::set<uint256> todo;
1270-
std::set<uint256> done;
1271-
12721267
// Can't mark abandoned if confirmed or in mempool
12731268
auto it = mapWallet.find(hashTx);
12741269
assert(it != mapWallet.end());
@@ -1277,44 +1272,25 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
12771272
return false;
12781273
}
12791274

1280-
todo.insert(hashTx);
1281-
1282-
while (!todo.empty()) {
1283-
uint256 now = *todo.begin();
1284-
todo.erase(now);
1285-
done.insert(now);
1286-
auto it = mapWallet.find(now);
1287-
assert(it != mapWallet.end());
1288-
CWalletTx& wtx = it->second;
1289-
int currentconfirm = GetTxDepthInMainChain(wtx);
1290-
// If the orig tx was not in block, none of its spends can be
1291-
assert(currentconfirm <= 0);
1292-
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
1293-
if (currentconfirm == 0 && !wtx.isAbandoned()) {
1294-
// If the orig tx was not in block/mempool, none of its spends can be in mempool
1295-
assert(!wtx.InMempool());
1275+
auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
1276+
// If the orig tx was not in block/mempool, none of its spends can be.
1277+
assert(!wtx.isConfirmed());
1278+
assert(!wtx.InMempool());
1279+
// If already conflicted or abandoned, no need to set abandoned
1280+
if (!wtx.isConflicted() && !wtx.isAbandoned()) {
12961281
wtx.m_state = TxStateInactive{/*abandoned=*/true};
1297-
wtx.MarkDirty();
1298-
batch.WriteTx(wtx);
1299-
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
1300-
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
1301-
// States are not permanent, so these transactions can become unabandoned if they are re-added to the
1302-
// mempool, or confirmed in a block, or conflicted.
1303-
// Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
1304-
// states change will remain abandoned and will require manual broadcast if the user wants them.
1305-
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
1306-
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
1307-
for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
1308-
if (!done.count(iter->second)) {
1309-
todo.insert(iter->second);
1310-
}
1311-
}
1312-
}
1313-
// If a transaction changes 'conflicted' state, that changes the balance
1314-
// available of the outputs it spends. So force those to be recomputed
1315-
MarkInputsDirty(wtx.tx);
1282+
return TxUpdate::NOTIFY_CHANGED;
13161283
}
1317-
}
1284+
return TxUpdate::UNCHANGED;
1285+
};
1286+
1287+
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
1288+
// States are not permanent, so these transactions can become unabandoned if they are re-added to the
1289+
// mempool, or confirmed in a block, or conflicted.
1290+
// Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
1291+
// states change will remain abandoned and will require manual broadcast if the user wants them.
1292+
1293+
RecursiveUpdateTxState(hashTx, try_updating_state);
13181294

13191295
return true;
13201296
}
@@ -1331,13 +1307,29 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
13311307
if (conflictconfirms >= 0)
13321308
return;
13331309

1310+
auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
1311+
if (conflictconfirms < GetTxDepthInMainChain(wtx)) {
1312+
// Block is 'more conflicted' than current confirm; update.
1313+
// Mark transaction as conflicted with this block.
1314+
wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
1315+
return TxUpdate::CHANGED;
1316+
}
1317+
return TxUpdate::UNCHANGED;
1318+
};
1319+
1320+
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too.
1321+
RecursiveUpdateTxState(hashTx, try_updating_state);
1322+
1323+
}
1324+
1325+
void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
13341326
// Do not flush the wallet here for performance reasons
13351327
WalletBatch batch(GetDatabase(), false);
13361328

13371329
std::set<uint256> todo;
13381330
std::set<uint256> done;
13391331

1340-
todo.insert(hashTx);
1332+
todo.insert(tx_hash);
13411333

13421334
while (!todo.empty()) {
13431335
uint256 now = *todo.begin();
@@ -1346,14 +1338,12 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
13461338
auto it = mapWallet.find(now);
13471339
assert(it != mapWallet.end());
13481340
CWalletTx& wtx = it->second;
1349-
int currentconfirm = GetTxDepthInMainChain(wtx);
1350-
if (conflictconfirms < currentconfirm) {
1351-
// Block is 'more conflicted' than current confirm; update.
1352-
// Mark transaction as conflicted with this block.
1353-
wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
1341+
1342+
TxUpdate update_state = try_updating_state(wtx);
1343+
if (update_state != TxUpdate::UNCHANGED) {
13541344
wtx.MarkDirty();
13551345
batch.WriteTx(wtx);
1356-
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
1346+
// Iterate over all its outputs, and update those tx states as well (if applicable)
13571347
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
13581348
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
13591349
for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
@@ -1362,7 +1352,12 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
13621352
}
13631353
}
13641354
}
1365-
// If a transaction changes 'conflicted' state, that changes the balance
1355+
1356+
if (update_state == TxUpdate::NOTIFY_CHANGED) {
1357+
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
1358+
}
1359+
1360+
// If a transaction changes its tx state, that usually changes the balance
13661361
// available of the outputs it spends. So force those to be recomputed
13671362
MarkInputsDirty(wtx.tx);
13681363
}

src/wallet/wallet.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,13 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
325325
/** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
326326
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
327327

328+
enum class TxUpdate { UNCHANGED, CHANGED, NOTIFY_CHANGED };
329+
330+
using TryUpdatingStateFn = std::function<TxUpdate(CWalletTx& wtx)>;
331+
332+
/** Mark a transaction (and its in-wallet descendants) as a particular tx state. */
333+
void RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
334+
328335
/** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
329336
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
330337

0 commit comments

Comments
 (0)