wallet: Determine IsFromMe by checking for TXOs of inputs
What changed, and why it matters
This commit fixes a wallet accounting bug. Previously, Bitcoin Core's wallet decided whether a transaction came from the user by checking whether the total value of its inputs was greater than zero. That approach missed inputs that are worth exactly zero (so-called 'dust' outputs). After the change, the wallet checks each input individually to see if it is known to the wallet, so even zero-value outputs are correctly recognized as spent. This is mainly a correctness fix for tracking tiny outputs and could affect how transactions are labeled, but it does not appear to be a direct theft-of-funds vulnerability.
Review related wallet logic that depends on IsFromMe (e.g., transaction listing, conflict detection, abandoned-transaction handling) to ensure the new behavior does not introduce regressions. Consider whether any RPC or GUI labels change for transactions that spend zero-value wallet outputs. No immediate emergency action is indicated.
Security signals we found
Logic error in wallet input attribution
Zero-value output handling edge case
Change in transaction ownership heuristic
Evidence from the diff
CWallet::IsFromMe() is changed from return (GetDebit(tx) > 0) to iterating over each CTxIn and returning true if any input’s prevout is found in the wallet via GetTXO(). GetDebit() sums input amounts and returns a CAmount, so a zero-value wallet input made the old test false. The new test uses the existence of the unspent transaction output in the wallet, independent of its value. This corrects the wallet’s ability to mark 0-value dust UTXOs as spent when they are consumed.
Changed components
src/wallet/wallet.cppCWallet::IsFromMeInspect captured patch +5 / −1
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6df74e84..b51e7d11 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1634,7 +1634,11 @@ bool CWallet::IsMine(const COutPoint& outpoint) const
bool CWallet::IsFromMe(const CTransaction& tx) const
{
- return (GetDebit(tx) > 0);
+ LOCK(cs_wallet);
+ for (const CTxIn& txin : tx.vin) {
+ if (GetTXO(txin.prevout)) return true;
+ }
+ return false;
}
CAmount CWallet::GetDebit(const CTransaction& tx) const
Why this scored 30/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.