Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
1501void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {1502LOCK(cs_wallet);1503auto it = mapWallet.find(tx->GetHash());1504if (it != mapWallet.end()) {1505RefreshMempoolStatus(it->second, chain());1506}1507// Handle transactions that were removed from the mempool because they1508// conflict with transactions in a newly connected block.1509if (reason == MemPoolRemovalReason::CONFLICT) {1510// Trigger external -walletnotify notifications for these transactions.1511// Set Status::UNCONFIRMED instead of Status::CONFLICTED for a few reasons:1512//1513// 1. The transactionRemovedFromMempool callback does not currently1514// provide the conflicting block's hash and height, and for backwards1515// compatibility reasons it may not be not safe to store conflicted1516// wallet transactions with a null block hash. See1517// https://github.com/bitcoin/bitcoin/pull/18600#discussion_r420195993.1518// 2. For most of these transactions, the wallet's internal conflict1519// detection in the blockConnected handler will subsequently call1520// MarkConflicted and update them with CONFLICTED status anyway. This1521// applies to any wallet transaction that has inputs spent in the1522// block, or that has ancestors in the wallet with inputs spent by1523// the block.1524// 3. Longstanding behavior since the sync implementation in1525// https://github.com/bitcoin/bitcoin/pull/9371 and the prior sync1526// implementation before that was to mark these transactions1527// unconfirmed rather than conflicted.1528//1529// Nothing described above should be seen as an unchangeable requirement1530// when improving this code in the future. The wallet's heuristics for1531// distinguishing between conflicted and unconfirmed transactions are1532// imperfect, and could be improved in general, see1533// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking1534SyncTransaction(tx, TxStateInactive{});1535}1537const Txid& txid = tx->GetHash();1539for (const CTxIn& tx_in : tx->vin) {1540// Iterate over all wallet transactions spending txin.prev1541// and recursively mark them as no longer conflicting with1542// txid1543for (auto range = mapTxSpends.equal_range(tx_in.prevout); range.first != range.second; range.first++) {1544const Txid& spent_id = range.first->second;1546RecursiveUpdateTxState(/*batch=*/nullptr, spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {1547return wtx.mempool_conflicts.erase(txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED;1548});1549}1550}1552if (tx->version == TRUC_VERSION) {1553// If this tx has a parent, unset its truc_child_in_mempool to make it possible1554// to spend from the parent again. If this tx was replaced by another1555// child of the same parent, transactionAddedToMempool1556// will update truc_child_in_mempool1557for (const CTxIn& tx_in : tx->vin) {1558auto parent_it = mapWallet.find(tx_in.prevout.hash);1559if (parent_it != mapWallet.end()) {1560CWalletTx& parent_wtx = parent_it->second;1561if (parent_wtx.truc_child_in_mempool == tx->GetHash()) {1562parent_wtx.truc_child_in_mempool = std::nullopt;1563UpdateTrucSiblingConflicts(parent_wtx, txid, /*add_conflict=*/false);1564}1565}1566}1567}1568}1570void CWallet::blockConnected(const ChainstateRole& role, const interfaces::BlockInfo& block)1571{1572if (role.historical) {1573return;1574}1575assert(block.data);1576LOCK(cs_wallet);1578// Update the best block in memory first. This will set the best block's height, which is1579// needed by MarkConflicted.1580SetLastBlockProcessedInMem(block.height, block.hash);1582// No need to scan block if it was created before the wallet birthday.1583// Uses chain max time and twice the grace period to adjust time for block time variability.1584if (block.chain_time_max < m_birth_time.load() - (TIMESTAMP_WINDOW * 2)) return;1586// Scan block1587bool wallet_updated = false;1588for (size_t index = 0; index < block.data->vtx.size(); index++) {1589wallet_updated |= SyncTransaction(block.data->vtx[index], TxStateConfirmed{block.hash, block.height, static_cast<int>(index)});1590transactionRemovedFromMempool(block.data->vtx[index], MemPoolRemovalReason::BLOCK);1591}1593// Update on disk if this block resulted in us updating a tx, or periodically every 144 blocks (~1 day)1594if (wallet_updated || block.height % 144 == 0) {1595WriteBestBlock();1596}1597}1599void CWallet::blockDisconnected(const interfaces::BlockInfo& block)1600{1601assert(block.data);1602LOCK(cs_wallet);1604// At block disconnection, this will change an abandoned transaction to1605// be unconfirmed, whether or not the transaction is added back to the mempool.1606// User may have to call abandontransaction again. It may be addressed in the1607// future with a stickier abandoned state or even removing abandontransaction call.1608int disconnect_height = block.height;1610for (size_t index = 0; index < block.data->vtx.size(); index++) {1611const CTransactionRef& ptx = block.data->vtx[index];1612// Coinbase transactions are not only inactive but also abandoned,1613// meaning they should never be relayed standalone via the p2p protocol.1614SyncTransaction(ptx, TxStateInactive{/*abandoned=*/index == 0});1616for (const CTxIn& tx_in : ptx->vin) {1617// No other wallet transactions conflicted with this transaction1618if (!mapTxSpends.contains(tx_in.prevout)) continue;1620std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(tx_in.prevout);1622// For all of the spends that conflict with this transaction1623for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) {1624CWalletTx& wtx = mapWallet.find(_it->second)->second;1626if (!wtx.isBlockConflicted()) continue;1628auto try_updating_state = [&](CWalletTx& tx) {1629if (!tx.isBlockConflicted()) return TxUpdate::UNCHANGED;1630if (tx.state<TxStateBlockConflicted>()->conflicting_block_height >= disconnect_height) {1631tx.m_state = TxStateInactive{};1632return TxUpdate::CHANGED;1633}1634return TxUpdate::UNCHANGED;1635};1637RecursiveUpdateTxState(wtx.GetTx()->GetHash(), try_updating_state);1638}1639}1640}1642// Update the best block1643SetLastBlockProcessed(block.height - 1, *Assert(block.prev_hash));1644}1646void CWallet::updatedBlockTip()1647{1648m_best_block_time = GetTime();1649}1651void CWallet::BlockUntilSyncedToCurrentChain() const {1652AssertLockNotHeld(cs_wallet);1653// Skip the queue-draining stuff if we know we're caught up with1654// chain().Tip(), otherwise put a callback in the validation interface queue and wait1655// for the queue to drain enough to execute it (indicating we are caught up1656// at least with the time we entered this function).1657uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);1658chain().waitForNotificationsIfTipChanged(last_block_hash);1659}1661// Note that this function doesn't distinguish between a 0-valued input,1662// and a not-"is mine" input.1663CAmount CWallet::GetDebit(const CTxIn &txin) const1664{1665LOCK(cs_wallet);1666auto txo = GetTXO(txin.prevout);1667if (txo) {1668return txo->GetTxOut().nValue;1669}1670return 0;1671}1673bool CWallet::IsMine(const CTxOut& txout) const1674{1675AssertLockHeld(cs_wallet);1676return IsMine(txout.scriptPubKey);1677}1679bool CWallet::IsMine(const CTxDestination& dest) const1680{1681AssertLockHeld(cs_wallet);1682return IsMine(GetScriptForDestination(dest));1683}1685bool CWallet::IsMine(const CScript& script) const1686{1687AssertLockHeld(cs_wallet);1689// Search the cache so that IsMine is called only on the relevant SPKMs instead of on everything in m_spk_managers1690const auto& it = m_cached_spks.find(script);1691if (it != m_cached_spks.end()) {1692bool res = false;1693for (const auto& spkm : it->second) {1694res = res || spkm->IsMine(script);1695}1696Assume(res);1697return res;1698}1700return false;1701}1703bool CWallet::IsMine(const CTransaction& tx) const1704{1705AssertLockHeld(cs_wallet);1706for (const CTxOut& txout : tx.vout)1707if (IsMine(txout))1708return true;1709return false;1710}1712bool CWallet::IsMine(const COutPoint& outpoint) const1713{1714AssertLockHeld(cs_wallet);1715auto wtx = GetWalletTx(outpoint.hash);1716if (!wtx) {1717return false;1718}1719if (outpoint.n >= wtx->GetTx()->vout.size()) {1720return false;1721}1722return IsMine(wtx->GetTx()->vout[outpoint.n]);1723}1725bool CWallet::IsFromMe(const CTransaction& tx) const1726{1727LOCK(cs_wallet);1728for (const CTxIn& txin : tx.vin) {1729if (GetTXO(txin.prevout)) return true;1730}1731return false;1732}1734CAmount CWallet::GetDebit(const CTransaction& tx) const1735{1736CAmount nDebit = 0;1737for (const CTxIn& txin : tx.vin)1738{1739nDebit += GetDebit(txin);1740if (!MoneyRange(nDebit))1741throw std::runtime_error(std::string(__func__) + ": value out of range");1742}1743return nDebit;1744}1746bool CWallet::IsHDEnabled() const1747{1748// All Active ScriptPubKeyMans must be HD for this to be true1749bool result = false;1750for (const auto& spk_man : GetActiveScriptPubKeyMans()) {