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.
751for (auto it = begin; it != end; ++it) {752auto entry = mapWallet.find(it->second);753if (!Assume(entry != mapWallet.end())) continue; // sanity-check: mapTxSpends has txs that are in mapWallet754const bool is_self = &entry->second == &wtx;755found_self |= is_self;756if (is_self || wtx.IsMalleation(entry->second)) {757Assume(txs.insert(&entry->second).second);758}759}760// wtx should always be found as this function is always called after AddToSpends761Assert(found_self);762return txs;763}765void CWallet::SyncMalleatedTxMetadata(WalletBatch& batch, const CWalletTx& wtx)766{767const auto txs = GetMalleatedVariants(wtx);768if (txs.size() <= 1) return; // no variants, nothing to do770// First tx is the oldest one (smallest nOrderPos)771const CWalletTx* copyFrom = *txs.begin();773// The metadata that is kept in sync between malleated variants.774// nTimeReceived, nOrderPos and cached members are not copied on purpose.775const auto metadata = [](auto& tx) {776return std::tie(tx.m_from, tx.m_message, tx.m_comment, tx.m_comment_to,777tx.m_replaces_txid, tx.m_replaced_by_txid,778tx.m_messages, tx.m_payment_requests, tx.nTimeSmart);779};781// Now copy data from copyFrom to rest:782for (CWalletTx* copyTo : txs) {783if (copyTo == copyFrom) continue;784metadata(*copyTo) = metadata(*copyFrom);785(void)batch.WriteTxMetadata(*copyTo);786}787}789/**790* Outpoint is spent if any non-conflicted transaction791* spends it:792*/793bool CWallet::IsSpent(const COutPoint& outpoint) const794{795std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;796range = mapTxSpends.equal_range(outpoint);798for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {799const Txid& txid = it->second;800const auto mit = mapWallet.find(txid);801if (mit != mapWallet.end()) {802const auto& wtx = mit->second;803if (!wtx.isAbandoned() && !wtx.isBlockConflicted() && !wtx.isMempoolConflicted())804return true; // Spent805}806}807return false;808}810CWallet::SpendType CWallet::HowSpent(const COutPoint& outpoint) const811{812SpendType st{SpendType::UNSPENT};814std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;815range = mapTxSpends.equal_range(outpoint);817for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {818const Txid& txid = it->second;819const auto mit = mapWallet.find(txid);820if (mit != mapWallet.end()) {821const auto& wtx = mit->second;822if (wtx.isConfirmed()) return SpendType::CONFIRMED;823if (wtx.InMempool()) {824st = SpendType::MEMPOOL;825} else if (!wtx.isAbandoned() && !wtx.isBlockConflicted() && !wtx.isMempoolConflicted()) {826if (st == SpendType::UNSPENT) st = SpendType::NONMEMPOOL;827}828}829}830return st;831}833void CWallet::AddToSpends(const COutPoint& outpoint, const Txid& txid)834{835mapTxSpends.insert(std::make_pair(outpoint, txid));837UnlockCoin(outpoint);838}841void CWallet::AddToSpends(const CWalletTx& wtx)842{843if (wtx.IsCoinBase()) // Coinbases don't spend anything!844return;846for (const CTxIn& txin : wtx.GetTx()->vin)847AddToSpends(txin.prevout, wtx.GetHash());848}850bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)851{852// Only descriptor wallets can be encrypted853Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));855if (HasEncryptionKeys())856return false;858CKeyingMaterial plain_master_key;860plain_master_key.resize(WALLET_CRYPTO_KEY_SIZE);861GetStrongRandBytes(plain_master_key);863CMasterKey master_key;865master_key.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);866GetStrongRandBytes(master_key.vchSalt);868if (!EncryptMasterKey(strWalletPassphrase, plain_master_key, master_key)) {869return false;870}871WalletLogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", master_key.nDeriveIterations);873{874LOCK2(m_relock_mutex, cs_wallet);875mapMasterKeys[++nMasterKeyMaxID] = master_key;876WalletBatch* encrypted_batch = new WalletBatch(GetDatabase());877if (!encrypted_batch->TxnBegin()) {878delete encrypted_batch;879encrypted_batch = nullptr;880return false;881}882encrypted_batch->WriteMasterKey(nMasterKeyMaxID, master_key);884for (const auto& spk_man_pair : m_spk_managers) {885auto spk_man = spk_man_pair.second.get();886if (!spk_man->Encrypt(plain_master_key, encrypted_batch)) {887encrypted_batch->TxnAbort();888delete encrypted_batch;889encrypted_batch = nullptr;890// We now probably have half of our keys encrypted in memory, and half not...891// die and let the user reload the unencrypted wallet.892assert(false);893}894}896if (!encrypted_batch->TxnCommit()) {897delete encrypted_batch;898encrypted_batch = nullptr;899// We now have keys encrypted in memory, but not on disk...900// die to avoid confusion and let the user reload the unencrypted wallet.901assert(false);902}904delete encrypted_batch;905encrypted_batch = nullptr;907Lock();908if (!Unlock(strWalletPassphrase)) {909return false;910}912SetupWalletGeneration();914Lock();916// Need to completely rewrite the wallet file; if we don't, the database might keep917// bits of the unencrypted private key in slack space in the database file.918GetDatabase().Rewrite();919}920NotifyStatusChanged(this);922return true;923}925DBErrors CWallet::ReorderTransactions()926{927LOCK(cs_wallet);928WalletBatch batch(GetDatabase());930// Old wallets didn't have any defined order for transactions931// Probably a bad idea to change the output of this933// First: get all CWalletTx into a sorted-by-time multimap.934typedef std::multimap<int64_t, CWalletTx*> TxItems;935TxItems txByTime;937for (auto& entry : mapWallet)938{939CWalletTx* wtx = &entry.second;940txByTime.insert(std::make_pair(wtx->nTimeReceived, wtx));941}943nOrderPosNext = 0;944std::vector<int64_t> nOrderPosOffsets;945for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)946{947CWalletTx *const pwtx = (*it).second;948int64_t& nOrderPos = pwtx->nOrderPos;950if (nOrderPos == -1)951{952nOrderPos = nOrderPosNext++;953nOrderPosOffsets.push_back(nOrderPos);955if (!batch.WriteTxMetadata(*pwtx))956return DBErrors::LOAD_FAIL;957}958else959{960int64_t nOrderPosOff = 0;961for (const int64_t& nOffsetStart : nOrderPosOffsets)962{963if (nOrderPos >= nOffsetStart)964++nOrderPosOff;965}966nOrderPos += nOrderPosOff;967nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);969if (!nOrderPosOff)970continue;972// Since we're changing the order, write it back973if (!batch.WriteTxMetadata(*pwtx))974return DBErrors::LOAD_FAIL;975}976}977batch.WriteOrderPosNext(nOrderPosNext);979return DBErrors::LOAD_OK;980}982int64_t CWallet::IncOrderPosNext(WalletBatch* batch)983{984AssertLockHeld(cs_wallet);985int64_t nRet = nOrderPosNext++;986if (batch) {987batch->WriteOrderPosNext(nOrderPosNext);988} else {989WalletBatch(GetDatabase()).WriteOrderPosNext(nOrderPosNext);990}991return nRet;992}994void CWallet::MarkDirty()995{996{997LOCK(cs_wallet);998for (auto& [_, wtx] : mapWallet)999wtx.MarkDirty();1000}