diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0814d672863..2db55273aec 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1234,6 +1234,23 @@ bool CWallet::TransactionCanBeAbandoned(const Txid& hashTx) const return wtx && !wtx->isAbandoned() && GetTxDepthInMainChain(*wtx) == 0 && !wtx->InMempool(); } +void CWallet::UpdateTrucSiblingConflicts(const CWalletTx& parent_wtx, const Txid& child_txid, bool add_conflict) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) +{ + // Find all other txs in our wallet that spend utxos from this parent + // so that we can mark them as mempool-conflicted by this new tx. + for (long unsigned int i = 0; i < parent_wtx.tx->vout.size(); i++) { + for (auto range = mapTxSpends.equal_range(COutPoint(parent_wtx.tx->GetHash(), i)); range.first != range.second; range.first++) { + const Txid& sibling_txid = range.first->second; + // Skip the child_tx itself + if (sibling_txid == child_txid) continue; + RecursiveUpdateTxState(/*batch=*/nullptr, sibling_txid, [&child_txid, add_conflict](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { + return add_conflict ? (wtx.mempool_conflicts.insert(child_txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED) + : (wtx.mempool_conflicts.erase(child_txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED); + }); + } + } +} + void CWallet::MarkInputsDirty(const CTransactionRef& tx) { for (const CTxIn& txin : tx->vin) { @@ -1402,6 +1419,9 @@ void CWallet::transactionAddedToMempool(const CTransactionRef& tx) { CWalletTx& parent_wtx = parent_it->second; if (parent_wtx.isUnconfirmed()) { parent_wtx.truc_child_in_mempool = tx->GetHash(); + // Even though these siblings do not spend the same utxos, they can't + // be present in the mempool at the same time because of TRUC policy rules + UpdateTrucSiblingConflicts(parent_wtx, txid, /*add_conflict=*/true); } } } @@ -1470,6 +1490,7 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe CWalletTx& parent_wtx = parent_it->second; if (parent_wtx.truc_child_in_mempool == tx->GetHash()) { parent_wtx.truc_child_in_mempool = std::nullopt; + UpdateTrucSiblingConflicts(parent_wtx, txid, /*add_conflict=*/false); } } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 5ef21025b96..baeb8943098 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -453,6 +453,9 @@ private: // Update last block processed in memory only void SetLastBlockProcessedInMem(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Update mempool conflicts for TRUC sibling transactions + void UpdateTrucSiblingConflicts(const CWalletTx& parent_wtx, const Txid& child_txid, bool add_conflict) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + public: /** * Main wallet lock.