Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2021-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <consensus/amount.h>6#include <consensus/consensus.h>7#include <util/check.h>8#include <wallet/receive.h>9#include <wallet/transaction.h>10#include <wallet/wallet.h>12namespace wallet {13bool InputIsMine(const CWallet& wallet, const CTxIn& txin)14{15AssertLockHeld(wallet.cs_wallet);16const CWalletTx* prev = wallet.GetWalletTx(txin.prevout.hash);17if (prev && txin.prevout.n < prev->GetTx()->vout.size()) {18return wallet.IsMine(prev->GetTx()->vout[txin.prevout.n]);19}20return false;21}23bool AllInputsMine(const CWallet& wallet, const CTransaction& tx)24{25LOCK(wallet.cs_wallet);26for (const CTxIn& txin : tx.vin) {27if (!InputIsMine(wallet, txin)) return false;28}29return true;30}32CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout)33{34if (!MoneyRange(txout.nValue))35throw std::runtime_error(std::string(__func__) + ": value out of range");36LOCK(wallet.cs_wallet);37return (wallet.IsMine(txout) ? txout.nValue : 0);38}40CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx)41{42CAmount nCredit = 0;43for (const CTxOut& txout : tx.vout)44{45nCredit += OutputGetCredit(wallet, txout);46if (!MoneyRange(nCredit))47throw std::runtime_error(std::string(__func__) + ": value out of range");48}49return nCredit;50}52bool ScriptIsChange(const CWallet& wallet, const CScript& script)53{54// TODO: fix handling of 'change' outputs. The assumption is that any55// payment to a script that is ours, but is not in the address book56// is change. That assumption is likely to break when we implement multisignature57// wallets that return change back into a multi-signature-protected address;58// a better way of identifying which outputs are 'the send' and which are59// 'the change' will need to be implemented (maybe extend CWalletTx to remember60// which output, if any, was change).61AssertLockHeld(wallet.cs_wallet);62if (wallet.IsMine(script))63{64CTxDestination address;65if (!ExtractDestination(script, address))66return true;67if (!wallet.FindAddressBookEntry(address)) {68return true;69}70}71return false;72}74bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)75{76return ScriptIsChange(wallet, txout.scriptPubKey);77}79CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout)80{81AssertLockHeld(wallet.cs_wallet);82if (!MoneyRange(txout.nValue))83throw std::runtime_error(std::string(__func__) + ": value out of range");84return (OutputIsChange(wallet, txout) ? txout.nValue : 0);85}87CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)88{89LOCK(wallet.cs_wallet);90CAmount nChange = 0;91for (const CTxOut& txout : tx.vout)92{93nChange += OutputGetChange(wallet, txout);94if (!MoneyRange(nChange))95throw std::runtime_error(std::string(__func__) + ": value out of range");96}97return nChange;98}100static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, bool avoid_reuse)101{102auto& amount = wtx.m_amounts[type];103if (!amount.IsCached(avoid_reuse)) {104amount.Set(avoid_reuse, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.GetTx()) : TxGetCredit(wallet, *wtx.GetTx()));105wtx.m_is_cache_empty = false;106}107return amount.Get(avoid_reuse);108}110CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)111{112AssertLockHeld(wallet.cs_wallet);114// Must wait until coinbase is safely deep enough in the chain before valuing it115if (wallet.IsTxImmatureCoinBase(wtx))116return 0;118// GetBalance can assume transactions in mapWallet won't change119return GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, avoid_reuse);120}122CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)123{124if (wtx.GetTx()->vin.empty())125return 0;127return GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, avoid_reuse);128}130CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)131{132if (wtx.fChangeCached)133return wtx.nChangeCached;134wtx.nChangeCached = TxGetChange(wallet, *wtx.GetTx());135wtx.fChangeCached = true;136return wtx.nChangeCached;137}139void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,140std::list<COutputEntry>& listReceived,141std::list<COutputEntry>& listSent, CAmount& nFee,142bool include_change)143{144nFee = 0;145listReceived.clear();146listSent.clear();148// Compute fee:149CAmount nDebit = CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/false);150if (nDebit > 0) // debit>0 means we signed/sent this transaction151{152CAmount nValueOut = wtx.GetTx()->GetValueOut();153nFee = nDebit - nValueOut;154}156LOCK(wallet.cs_wallet);157// Sent/received.158for (unsigned int i = 0; i < wtx.GetTx()->vout.size(); ++i)159{160const CTxOut& txout = wtx.GetTx()->vout[i];161bool ismine = wallet.IsMine(txout);162// Only need to handle txouts if AT LEAST one of these is true:163// 1) they debit from us (sent)164// 2) the output is to us (received)165if (nDebit > 0)166{167if (!include_change && OutputIsChange(wallet, txout))168continue;169}170else if (!ismine)171continue;173// In either case, we need to get the destination address174CTxDestination address;176if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())177{178wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",179wtx.GetHash().ToString());180address = CNoDestination();181}183COutputEntry output = {address, txout.nValue, (int)i};185// If we are debited by the transaction, add the output as a "sent" entry186if (nDebit > 0)187listSent.push_back(output);189// If we are receiving the output, add it as a "received" entry190if (ismine)191listReceived.push_back(output);192}194}196bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx)197{198if (!wtx.m_cached_from_me.has_value()) {199wtx.m_cached_from_me = wallet.IsFromMe(*wtx.GetTx());200}201return wtx.m_cached_from_me.value();202}204// NOLINTNEXTLINE(misc-no-recursion)205bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<Txid>& trusted_parents)206{207AssertLockHeld(wallet.cs_wallet);209// This wtx is already trusted210if (trusted_parents.contains(wtx.GetHash())) return true;212if (wtx.isConfirmed()) return true;213if (wtx.isBlockConflicted()) return false;214// using wtx's cached debit215if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx)) return false;217// Don't trust unconfirmed transactions from us unless they are in the mempool.218if (!wtx.InMempool()) return false;220// Trusted if all inputs are from us and are in the mempool:221for (const CTxIn& txin : wtx.GetTx()->vin)222{223// Transactions not sent by us: not trusted224const CWalletTx* parent = wallet.GetWalletTx(txin.prevout.hash);225if (parent == nullptr) return false;226const CTxOut& parentOut = parent->GetTx()->vout[txin.prevout.n];227// Check that this specific input being spent is trusted228if (!wallet.IsMine(parentOut)) return false;229// If we've already trusted this parent, continue230if (trusted_parents.contains(parent->GetHash())) continue;231// Recurse to check that the parent is also trusted232if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) return false;233trusted_parents.insert(parent->GetHash());234}235return true;236}238bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)239{240std::set<Txid> trusted_parents;241LOCK(wallet.cs_wallet);242return CachedTxIsTrusted(wallet, wtx, trusted_parents);243}245Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse, bool include_nonmempool)246{247Balance ret;248bool allow_used_addresses = !avoid_reuse || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);249{250LOCK(wallet.cs_wallet);