mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
wallet: introduce generic recursive tx state updating function
This commit also changed `MarkConflicted` and `AbandonTransaction` to use this new function Co-authored-by: ariard <antoine.riard@gmail.com>
This commit is contained in:
parent
49d07ea9a1
commit
096487c4dc
2 changed files with 51 additions and 49 deletions
|
@ -1264,11 +1264,6 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
|
||||||
WalletBatch batch(GetDatabase());
|
|
||||||
|
|
||||||
std::set<uint256> todo;
|
|
||||||
std::set<uint256> done;
|
|
||||||
|
|
||||||
// Can't mark abandoned if confirmed or in mempool
|
// Can't mark abandoned if confirmed or in mempool
|
||||||
auto it = mapWallet.find(hashTx);
|
auto it = mapWallet.find(hashTx);
|
||||||
assert(it != mapWallet.end());
|
assert(it != mapWallet.end());
|
||||||
|
@ -1277,44 +1272,25 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
todo.insert(hashTx);
|
auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
|
||||||
|
// If the orig tx was not in block/mempool, none of its spends can be.
|
||||||
while (!todo.empty()) {
|
assert(!wtx.isConfirmed());
|
||||||
uint256 now = *todo.begin();
|
assert(!wtx.InMempool());
|
||||||
todo.erase(now);
|
// If already conflicted or abandoned, no need to set abandoned
|
||||||
done.insert(now);
|
if (!wtx.isConflicted() && !wtx.isAbandoned()) {
|
||||||
auto it = mapWallet.find(now);
|
|
||||||
assert(it != mapWallet.end());
|
|
||||||
CWalletTx& wtx = it->second;
|
|
||||||
int currentconfirm = GetTxDepthInMainChain(wtx);
|
|
||||||
// If the orig tx was not in block, none of its spends can be
|
|
||||||
assert(currentconfirm <= 0);
|
|
||||||
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
|
|
||||||
if (currentconfirm == 0 && !wtx.isAbandoned()) {
|
|
||||||
// If the orig tx was not in block/mempool, none of its spends can be in mempool
|
|
||||||
assert(!wtx.InMempool());
|
|
||||||
wtx.m_state = TxStateInactive{/*abandoned=*/true};
|
wtx.m_state = TxStateInactive{/*abandoned=*/true};
|
||||||
wtx.MarkDirty();
|
return TxUpdate::NOTIFY_CHANGED;
|
||||||
batch.WriteTx(wtx);
|
|
||||||
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
|
|
||||||
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
|
|
||||||
// States are not permanent, so these transactions can become unabandoned if they are re-added to the
|
|
||||||
// mempool, or confirmed in a block, or conflicted.
|
|
||||||
// Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
|
|
||||||
// states change will remain abandoned and will require manual broadcast if the user wants them.
|
|
||||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
|
||||||
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
|
|
||||||
for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
|
|
||||||
if (!done.count(iter->second)) {
|
|
||||||
todo.insert(iter->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If a transaction changes 'conflicted' state, that changes the balance
|
|
||||||
// available of the outputs it spends. So force those to be recomputed
|
|
||||||
MarkInputsDirty(wtx.tx);
|
|
||||||
}
|
}
|
||||||
}
|
return TxUpdate::UNCHANGED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
|
||||||
|
// States are not permanent, so these transactions can become unabandoned if they are re-added to the
|
||||||
|
// mempool, or confirmed in a block, or conflicted.
|
||||||
|
// Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
|
||||||
|
// states change will remain abandoned and will require manual broadcast if the user wants them.
|
||||||
|
|
||||||
|
RecursiveUpdateTxState(hashTx, try_updating_state);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1331,13 +1307,29 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
|
||||||
if (conflictconfirms >= 0)
|
if (conflictconfirms >= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
|
||||||
|
if (conflictconfirms < GetTxDepthInMainChain(wtx)) {
|
||||||
|
// Block is 'more conflicted' than current confirm; update.
|
||||||
|
// Mark transaction as conflicted with this block.
|
||||||
|
wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
|
||||||
|
return TxUpdate::CHANGED;
|
||||||
|
}
|
||||||
|
return TxUpdate::UNCHANGED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too.
|
||||||
|
RecursiveUpdateTxState(hashTx, try_updating_state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
|
||||||
// Do not flush the wallet here for performance reasons
|
// Do not flush the wallet here for performance reasons
|
||||||
WalletBatch batch(GetDatabase(), false);
|
WalletBatch batch(GetDatabase(), false);
|
||||||
|
|
||||||
std::set<uint256> todo;
|
std::set<uint256> todo;
|
||||||
std::set<uint256> done;
|
std::set<uint256> done;
|
||||||
|
|
||||||
todo.insert(hashTx);
|
todo.insert(tx_hash);
|
||||||
|
|
||||||
while (!todo.empty()) {
|
while (!todo.empty()) {
|
||||||
uint256 now = *todo.begin();
|
uint256 now = *todo.begin();
|
||||||
|
@ -1346,14 +1338,12 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
|
||||||
auto it = mapWallet.find(now);
|
auto it = mapWallet.find(now);
|
||||||
assert(it != mapWallet.end());
|
assert(it != mapWallet.end());
|
||||||
CWalletTx& wtx = it->second;
|
CWalletTx& wtx = it->second;
|
||||||
int currentconfirm = GetTxDepthInMainChain(wtx);
|
|
||||||
if (conflictconfirms < currentconfirm) {
|
TxUpdate update_state = try_updating_state(wtx);
|
||||||
// Block is 'more conflicted' than current confirm; update.
|
if (update_state != TxUpdate::UNCHANGED) {
|
||||||
// Mark transaction as conflicted with this block.
|
|
||||||
wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
|
|
||||||
wtx.MarkDirty();
|
wtx.MarkDirty();
|
||||||
batch.WriteTx(wtx);
|
batch.WriteTx(wtx);
|
||||||
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
|
// Iterate over all its outputs, and update those tx states as well (if applicable)
|
||||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
||||||
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
|
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
|
||||||
for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If a transaction changes 'conflicted' state, that changes the balance
|
|
||||||
|
if (update_state == TxUpdate::NOTIFY_CHANGED) {
|
||||||
|
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a transaction changes its tx state, that usually changes the balance
|
||||||
// available of the outputs it spends. So force those to be recomputed
|
// available of the outputs it spends. So force those to be recomputed
|
||||||
MarkInputsDirty(wtx.tx);
|
MarkInputsDirty(wtx.tx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,6 +325,13 @@ private:
|
||||||
/** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
|
/** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
|
||||||
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
|
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
|
||||||
|
|
||||||
|
enum class TxUpdate { UNCHANGED, CHANGED, NOTIFY_CHANGED };
|
||||||
|
|
||||||
|
using TryUpdatingStateFn = std::function<TxUpdate(CWalletTx& wtx)>;
|
||||||
|
|
||||||
|
/** Mark a transaction (and its in-wallet descendants) as a particular tx state. */
|
||||||
|
void RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||||
|
|
||||||
/** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
|
/** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
|
||||||
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue