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.
1001}1003bool CWallet::MarkReplaced(const Txid& originalHash, const Txid& newHash)1004{1005LOCK(cs_wallet);1007auto mi = mapWallet.find(originalHash);1009// There is a bug if MarkReplaced is not called on an existing wallet transaction.1010assert(mi != mapWallet.end());1012CWalletTx& wtx = (*mi).second;1014// Ensure for now that we're not overwriting data1015Assert(!wtx.m_replaced_by_txid);1017wtx.m_replaced_by_txid = newHash;1019// Refresh mempool status without waiting for transactionRemovedFromMempool or transactionAddedToMempool1020RefreshMempoolStatus(wtx, chain());1022WalletBatch batch(GetDatabase());1024bool success = true;1025if (!batch.WriteTxMetadata(wtx)) {1026WalletLogPrintf("%s: Updating batch tx %s failed\n", __func__, wtx.GetHash().ToString());1027success = false;1028}1030// The new transaction also replaces any malleated variants of wtx,1031// so bumpfee refuses to bump them afterwards1032for (CWalletTx* variant : GetMalleatedVariants(wtx)) {1033if (variant == &wtx) continue;1034variant->m_replaced_by_txid = newHash;1035if (!batch.WriteTxMetadata(*variant)) {1036WalletLogPrintf("%s: Updating variant tx %s failed\n", __func__, variant->GetHash().ToString());1037success = false;1038}1039}1041NotifyTransactionChanged(originalHash, CT_UPDATED);1043return success;1044}1046void CWallet::SetSpentKeyState(WalletBatch& batch, const Txid& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)1047{1048AssertLockHeld(cs_wallet);1049const CWalletTx* srctx = GetWalletTx(hash);1050if (!srctx) return;1052CTxDestination dst;1053if (ExtractDestination(srctx->GetTx()->vout[n].scriptPubKey, dst)) {1054if (IsMine(dst)) {1055if (used != IsAddressPreviouslySpent(dst)) {1056if (used) {1057tx_destinations.insert(dst);1058}1059SetAddressPreviouslySpent(batch, dst, used);1060}1061}1062}1063}1065bool CWallet::IsSpentKey(const CScript& scriptPubKey) const1066{1067AssertLockHeld(cs_wallet);1068CTxDestination dest;1069if (!ExtractDestination(scriptPubKey, dest)) {1070return false;1071}1072if (IsAddressPreviouslySpent(dest)) {1073return true;1074}1075return false;1076}1078CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx, bool rescanning_old_block)1079{1080LOCK(cs_wallet);1082WalletBatch batch(GetDatabase());1084Txid hash = tx->GetHash();1086if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {1087// Mark used destinations1088std::set<CTxDestination> tx_destinations;1090for (const CTxIn& txin : tx->vin) {1091const COutPoint& op = txin.prevout;1092SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);1093}1095MarkDestinationsDirty(tx_destinations);1096}1098// Inserts only if not already there, returns tx inserted or tx found1099auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(tx, state));1100CWalletTx& wtx = (*ret.first).second;1101bool fInsertedNew = ret.second;1102bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew);1103if (fInsertedNew) {1104wtx.nTimeReceived = GetTime();1105wtx.nOrderPos = IncOrderPosNext(&batch);1106wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));1107wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);1108AddToSpends(wtx);1109SyncMalleatedTxMetadata(batch, wtx);1111// Update birth time when tx time is older than it.1112MaybeUpdateBirthTime(wtx.GetTxTime());1114if (!batch.WriteFullTx(wtx)) {1115return nullptr;1116}1117}1119if (!fInsertedNew)1120{1121try {1122fUpdated |= wtx.Update(tx, state, batch, fUpdated);1123} catch (const std::ios_base::failure& e) {1124WalletLogPrintf("Error: Unable to write tx update, %s", e.what());1125return nullptr;1126}1127}1129// Mark inactive coinbase transactions and their descendants as abandoned1130if (wtx.IsCoinBase() && wtx.isInactive()) {1131std::vector<CWalletTx*> txs{&wtx};1133TxStateInactive inactive_state = TxStateInactive{/*abandoned=*/true};1135while (!txs.empty()) {1136CWalletTx* desc_tx = txs.back();1137txs.pop_back();1138desc_tx->m_state = inactive_state;1139// Break caches since we have changed the state1140desc_tx->MarkDirty();1141batch.WriteTxMetadata(*desc_tx);1142MarkInputsDirty(desc_tx->GetTx());1143for (unsigned int i = 0; i < desc_tx->GetTx()->vout.size(); ++i) {1144COutPoint outpoint(desc_tx->GetHash(), i);1145std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(outpoint);1146for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {1147const auto wit = mapWallet.find(it->second);1148if (wit != mapWallet.end()) {1149txs.push_back(&wit->second);1150}1151}1152}1153}1154}1156//// debug print1157std::string status{"no-change"};1158if (fInsertedNew || fUpdated) {1159status = fInsertedNew ? (fUpdated ? "new, update" : "new") : "update";1160}1161WalletLogPrintf("AddToWallet %s %s %s", hash.ToString(), status, TxStateString(state));1163// Break debit/credit balance caches:1164wtx.MarkDirty();1166// Cache the outputs that belong to the wallet1167RefreshTXOsFromTx(wtx);1169// Notify UI of new or updated transaction1170NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED);1172#if HAVE_SYSTEM1173// notify an external script when a wallet transaction comes in or is updated1174std::string strCmd = m_notify_tx_changed_script;1176if (!strCmd.empty())1177{1178ReplaceAll(strCmd, "%s", hash.GetHex());1179if (auto* conf = wtx.state<TxStateConfirmed>())1180{1181ReplaceAll(strCmd, "%b", conf->confirmed_block_hash.GetHex());1182ReplaceAll(strCmd, "%h", ToString(conf->confirmed_block_height));1183} else {1184ReplaceAll(strCmd, "%b", "unconfirmed");1185ReplaceAll(strCmd, "%h", "-1");1186}1187#ifndef WIN321188// Substituting the wallet name isn't currently supported on windows1189// because windows shell escaping has not been implemented yet:1190// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-5373848751191// A few ways it could be implemented in the future are described in:1192// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-4612880941193ReplaceAll(strCmd, "%w", ShellEscape(GetName()));1194#endif1195std::thread t(runCommand, strCmd);1196t.detach(); // thread runs free1197}1198#endif1200return &wtx;1201}1203bool CWallet::LoadToWallet(CWalletTx&& wtx_in)1204{1205const auto& ins = mapWallet.emplace(wtx_in.GetHash(), std::move(wtx_in));1206CWalletTx& wtx = ins.first->second;1207if (!ins.second) {1208return false;1209}1210// If wallet doesn't have a chain (e.g when using bitcoin-wallet tool),1211// don't bother to update txn.1212if (HaveChain()) {1213wtx.updateState(chain());1214}1215wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));1216AddToSpends(wtx);1217for (const CTxIn& txin : wtx.GetTx()->vin) {1218auto it = mapWallet.find(txin.prevout.hash);1219if (it != mapWallet.end()) {1220CWalletTx& prevtx = it->second;1221if (auto* prev = prevtx.state<TxStateBlockConflicted>()) {1222MarkConflicted(prev->conflicting_block_hash, prev->conflicting_block_height, wtx.GetHash());1223}1224}1225}1227// Update birth time when tx time is older than it.1228MaybeUpdateBirthTime(wtx.GetTxTime());1230// Make sure the tx outputs are known by the wallet1231RefreshTXOsFromTx(wtx);1232return true;1233}1235bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxState& state, bool rescanning_old_block)1236{1237const CTransaction& tx = *ptx;1238{1239AssertLockHeld(cs_wallet);1241if (auto* conf = std::get_if<TxStateConfirmed>(&state)) {1242for (const CTxIn& txin : tx.vin) {1243std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);1244while (range.first != range.second) {1245if (range.first->second != tx.GetHash()) {1246WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), conf->confirmed_block_hash.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);1247MarkConflicted(conf->confirmed_block_hash, conf->confirmed_block_height, range.first->second);1248}1249range.first++;1250}