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.
1251}1252}1254bool fExisted = mapWallet.contains(tx.GetHash());1255if (fExisted || IsMine(tx) || IsFromMe(tx))1256{1257/* Check if any keys in the wallet keypool that were supposed to be unused1258* have appeared in a new transaction. If so, remove those keys from the keypool.1259* This can happen when restoring an old wallet backup that does not contain1260* the mostly recently created transactions from newer versions of the wallet.1261*/1263// loop though all outputs1264for (const CTxOut& txout: tx.vout) {1265for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) {1266for (auto &dest : spk_man->MarkUnusedAddresses(txout.scriptPubKey)) {1267// If internal flag is not defined try to infer it from the ScriptPubKeyMan1268if (!dest.internal.has_value()) {1269dest.internal = IsInternalScriptPubKeyMan(spk_man);1270}1272// skip if can't determine whether it's a receiving address or not1273if (!dest.internal.has_value()) continue;1275// If this is a receiving address and it's not in the address book yet1276// (e.g. it wasn't generated on this node or we're restoring from backup)1277// add it to the address book for proper transaction accounting1278if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) {1279SetAddressBook(dest.dest, "", AddressPurpose::RECEIVE);1280}1281}1282}1283}1285// Block disconnection override an abandoned tx as unconfirmed1286// which means user may have to call abandontransaction again1287TxState tx_state = std::visit([](auto&& s) -> TxState { return s; }, state);1288CWalletTx* wtx = AddToWallet(MakeTransactionRef(tx), tx_state, /*update_wtx=*/nullptr, rescanning_old_block);1289if (!wtx) {1290// Can only be nullptr if there was a db write error (missing db, read-only db or a db engine internal writing error).1291// As we only store arriving transaction in this process, and we don't want an inconsistent state, let's throw an error.1292throw std::runtime_error("DB error adding transaction to wallet, write failed");1293}1294return true;1295}1296}1297return false;1298}1300bool CWallet::TransactionCanBeAbandoned(const Txid& hashTx) const1301{1302LOCK(cs_wallet);1303const CWalletTx* wtx = GetWalletTx(hashTx);1304return wtx && !wtx->isAbandoned() && GetTxDepthInMainChain(*wtx) == 0 && !wtx->InMempool();1305}1307void CWallet::UpdateTrucSiblingConflicts(const CWalletTx& parent_wtx, const Txid& child_txid, bool add_conflict) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)1308{1309// Find all other txs in our wallet that spend utxos from this parent1310// so that we can mark them as mempool-conflicted by this new tx.1311for (long unsigned int i = 0; i < parent_wtx.GetTx()->vout.size(); i++) {1312for (auto range = mapTxSpends.equal_range(COutPoint(parent_wtx.GetTx()->GetHash(), i)); range.first != range.second; range.first++) {1313const Txid& sibling_txid = range.first->second;1314// Skip the child_tx itself1315if (sibling_txid == child_txid) continue;1316RecursiveUpdateTxState(/*batch=*/nullptr, sibling_txid, [&child_txid, add_conflict](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {1317return add_conflict ? (wtx.mempool_conflicts.insert(child_txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED)1318: (wtx.mempool_conflicts.erase(child_txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED);1319});1320}1321}1322}1324void CWallet::MarkInputsDirty(const CTransactionRef& tx)1325{1326for (const CTxIn& txin : tx->vin) {1327auto it = mapWallet.find(txin.prevout.hash);1328if (it != mapWallet.end()) {1329it->second.MarkDirty();1330}1331}1332}1334bool CWallet::AbandonTransaction(const Txid& hashTx)1335{1336LOCK(cs_wallet);1337auto it = mapWallet.find(hashTx);1338assert(it != mapWallet.end());1339return AbandonTransaction(it->second);1340}1342bool CWallet::AbandonTransaction(CWalletTx& tx)1343{1344// Can't mark abandoned if confirmed or in mempool1345if (GetTxDepthInMainChain(tx) != 0 || tx.InMempool()) {1346return false;1347}1349auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {1350// If the orig tx was not in block/mempool, none of its spends can be.1351assert(!wtx.isConfirmed());1352assert(!wtx.InMempool());1353// If already conflicted or abandoned, no need to set abandoned1354if (!wtx.isBlockConflicted() && !wtx.isAbandoned()) {1355wtx.m_state = TxStateInactive{/*abandoned=*/true};1356return TxUpdate::NOTIFY_CHANGED;1357}1358return TxUpdate::UNCHANGED;1359};1361// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.1362// States are not permanent, so these transactions can become unabandoned if they are re-added to the1363// mempool, or confirmed in a block, or conflicted.1364// Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their1365// states change will remain abandoned and will require manual broadcast if the user wants them.1367RecursiveUpdateTxState(tx.GetHash(), try_updating_state);1369return true;1370}1372void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const Txid& hashTx)1373{1374LOCK(cs_wallet);1376// If number of conflict confirms cannot be determined, this means1377// that the block is still unknown or not yet part of the main chain,1378// for example when loading the wallet during a reindex. Do nothing in that1379// case.1380if (m_last_block_processed_height < 0 || conflicting_height < 0) {1381return;1382}1383int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1;1384if (conflictconfirms >= 0)1385return;1387auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {1388if (conflictconfirms < GetTxDepthInMainChain(wtx)) {1389// Block is 'more conflicted' than current confirm; update.1390// Mark transaction as conflicted with this block.1391wtx.m_state = TxStateBlockConflicted{hashBlock, conflicting_height};1392return TxUpdate::CHANGED;1393}1394return TxUpdate::UNCHANGED;1395};1397// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too.1398RecursiveUpdateTxState(hashTx, try_updating_state);1400}1402void CWallet::RecursiveUpdateTxState(const Txid& tx_hash, const TryUpdatingStateFn& try_updating_state) {1403WalletBatch batch(GetDatabase());1404RecursiveUpdateTxState(&batch, tx_hash, try_updating_state);1405}1407void CWallet::RecursiveUpdateTxState(WalletBatch* batch, const Txid& tx_hash, const TryUpdatingStateFn& try_updating_state) {1408std::set<Txid> todo;1409std::set<Txid> done;1411todo.insert(tx_hash);1413while (!todo.empty()) {1414Txid now = *todo.begin();1415todo.erase(now);1416done.insert(now);1417auto it = mapWallet.find(now);1418assert(it != mapWallet.end());1419CWalletTx& wtx = it->second;1421TxUpdate update_state = try_updating_state(wtx);1422if (update_state != TxUpdate::UNCHANGED) {1423wtx.MarkDirty();1424if (batch) batch->WriteTxMetadata(wtx);1425// Iterate over all its outputs, and update those tx states as well (if applicable)1426for (unsigned int i = 0; i < wtx.GetTx()->vout.size(); ++i) {1427std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));1428for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {1429if (!done.contains(iter->second)) {1430todo.insert(iter->second);1431}1432}1433}1435if (update_state == TxUpdate::NOTIFY_CHANGED) {1436NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);1437}1439// If a transaction changes its tx state, that usually changes the balance1440// available of the outputs it spends. So force those to be recomputed1441MarkInputsDirty(wtx.GetTx());1442}1443}1444}1446bool CWallet::SyncTransaction(const CTransactionRef& ptx, const SyncTxState& state, bool rescanning_old_block)1447{1448if (!AddToWalletIfInvolvingMe(ptx, state, rescanning_old_block))1449return false; // Not one of ours1451// If a transaction changes 'conflicted' state, that changes the balance1452// available of the outputs it spends. So force those to be1453// recomputed, also:1454MarkInputsDirty(ptx);1455return true;1456}1458void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {1459LOCK(cs_wallet);1460SyncTransaction(tx, TxStateInMempool{});1462auto it = mapWallet.find(tx->GetHash());1463if (it != mapWallet.end()) {1464RefreshMempoolStatus(it->second, chain());1465}1467const Txid& txid = tx->GetHash();1469for (const CTxIn& tx_in : tx->vin) {1470// For each wallet transaction spending this prevout..1471for (auto range = mapTxSpends.equal_range(tx_in.prevout); range.first != range.second; range.first++) {1472const Txid& spent_id = range.first->second;1473// Skip the recently added tx1474if (spent_id == txid) continue;1475RecursiveUpdateTxState(/*batch=*/nullptr, spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {1476return wtx.mempool_conflicts.insert(txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED;1477});1478}1480}1482if (tx->version == TRUC_VERSION) {1483// Unconfirmed TRUC transactions are only allowed a 1-parent-1-child topology.1484// For any unconfirmed v3 parents (there should be a maximum of 1 except in reorgs),1485// record this child so the wallet doesn't try to spend any other outputs1486for (const CTxIn& tx_in : tx->vin) {1487auto parent_it = mapWallet.find(tx_in.prevout.hash);1488if (parent_it != mapWallet.end()) {1489CWalletTx& parent_wtx = parent_it->second;1490if (parent_wtx.isUnconfirmed()) {1491parent_wtx.truc_child_in_mempool = tx->GetHash();1492// Even though these siblings do not spend the same utxos, they can't1493// be present in the mempool at the same time because of TRUC policy rules1494UpdateTrucSiblingConflicts(parent_wtx, txid, /*add_conflict=*/true);1495}1496}1497}1498}1499}