wallet: track mempool conflicts

Behavior changes are:
- if a tx has a mempool conflict, the wallet will not attempt to
  rebroadcast it
- if a txo is spent by a mempool-conflicted tx, that txo is no
  longer considered spent
This commit is contained in:
ishaanam
2023-05-17 20:56:25 -04:00
parent d64922b590
commit 54e07ee22f
5 changed files with 57 additions and 11 deletions

View File

@@ -753,7 +753,7 @@ bool CWallet::IsSpent(const COutPoint& outpoint) const
const auto mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
const auto& wtx = mit->second;
if (!wtx.isAbandoned() && !wtx.isBlockConflicted())
if (!wtx.isAbandoned() && !wtx.isBlockConflicted() && !wtx.isMempoolConflicted())
return true; // Spent
}
}
@@ -1360,7 +1360,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
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
WalletBatch batch(GetDatabase(), false);
RecursiveUpdateTxState(&batch, tx_hash, try_updating_state);
}
void CWallet::RecursiveUpdateTxState(WalletBatch* batch, const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
std::set<uint256> todo;
std::set<uint256> done;
@@ -1377,7 +1380,7 @@ void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingSt
TxUpdate update_state = try_updating_state(wtx);
if (update_state != TxUpdate::UNCHANGED) {
wtx.MarkDirty();
batch.WriteTx(wtx);
if (batch) batch->WriteTx(wtx);
// 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) {
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) {
if (it != mapWallet.end()) {
RefreshMempoolStatus(it->second, chain());
}
const Txid& txid = tx->GetHash();
for (const CTxIn& tx_in : tx->vin) {
// For each wallet transaction spending this prevout..
for (auto range = mapTxSpends.equal_range(tx_in.prevout); range.first != range.second; range.first++) {
const uint256& spent_id = range.first->second;
// Skip the recently added tx
if (spent_id == txid) continue;
RecursiveUpdateTxState(/*batch=*/nullptr, spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
return wtx.mempool_conflicts.insert(txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED;
});
}
}
}
void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
@@ -1455,6 +1472,21 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
SyncTransaction(tx, TxStateInactive{});
}
const Txid& txid = tx->GetHash();
for (const CTxIn& tx_in : tx->vin) {
// Iterate over all wallet transactions spending txin.prev
// and recursively mark them as no longer conflicting with
// txid
for (auto range = mapTxSpends.equal_range(tx_in.prevout); range.first != range.second; range.first++) {
const uint256& spent_id = range.first->second;
RecursiveUpdateTxState(/*batch=*/nullptr, spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
return wtx.mempool_conflicts.erase(txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED;
});
}
}
}
void CWallet::blockConnected(ChainstateRole role, const interfaces::BlockInfo& block)