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.
3251}3253// Register wallet with validationinterface. It's done before rescan to avoid3254// missing block connections during the rescan.3255// Because of the wallet lock being held, block connection notifications are going to3256// be pending on the validation-side until lock release. Blocks that are connected while the3257// rescan is ongoing will not be processed in the rescan but with the block connected notifications,3258// so the wallet will only be completeley synced after the notifications delivery.3259walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);3261// If rescan_required = true, rescan_height remains equal to 03262int rescan_height = 0;3263if (!rescan_required)3264{3265WalletBatch batch(walletInstance->GetDatabase());3266CBlockLocator locator;3267if (batch.ReadBestBlock(locator)) {3268if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) {3269rescan_height = *fork_height;3270}3271}3272}3274const std::optional<int> tip_height = chain.getHeight();3275if (tip_height) {3276walletInstance->SetLastBlockProcessedInMem(*tip_height, chain.getBlockHash(*tip_height));3277} else {3278walletInstance->SetLastBlockProcessedInMem(-1, uint256());3279}3281if (tip_height && *tip_height != rescan_height)3282{3283// No need to read and scan block if block was created before3284// our wallet birthday (as adjusted for block time variability)3285std::optional<int64_t> time_first_key = walletInstance->m_birth_time.load();3286if (time_first_key) {3287FoundBlock found = FoundBlock().height(rescan_height);3288chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, found);3289if (!found.found) {3290// We were unable to find a block that had a time more recent than our earliest timestamp3291// or a height higher than the wallet was synced to, indicating that the wallet is newer than the3292// current chain tip. Skip rescanning in this case.3293rescan_height = *tip_height;3294}3295}3297// Technically we could execute the code below in any case, but performing the3298// `while` loop below can make startup very slow, so only check blocks on disk3299// if necessary.3300if (chain.havePruned() || chain.hasAssumedValidChain()) {3301int block_height = *tip_height;3302while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {3303--block_height;3304}3306if (rescan_height != block_height) {3307// We can't rescan beyond blocks we don't have data for, stop and throw an error.3308// This might happen if a user uses an old wallet within a pruned node3309// or if they ran -disablewallet for a longer time, then decided to re-enable3310// Exit early and print an error.3311// It also may happen if an assumed-valid chain is in use and therefore not3312// all block data is available.3313// If a block is pruned after this check, we will load the wallet,3314// but fail the rescan with a generic error.3316error = chain.havePruned() ?3317_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of a pruned node)") :3318strprintf(_(3319"Error loading wallet. Wallet requires blocks to be downloaded, "3320"and software does not currently support loading wallets while "3321"blocks are being downloaded out of order when using assumeutxo "3322"snapshots. Wallet should be able to load successfully after "3323"node sync reaches height %s"), block_height);3324return false;3325}3326}3328chain.initMessage(_("Rescanning…"));3329walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);3331{3332WalletRescanReserver reserver(*walletInstance);3333if (!reserver.reserve()) {3334error = _("Failed to acquire rescan reserver during wallet initialization");3335return false;3336}3337ScanResult scan_res = walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, /*max_height=*/{}, reserver, /*save_progress=*/true);3338if (ScanResult::SUCCESS != scan_res.status) {3339error = _("Failed to rescan the wallet during initialization");3340return false;3341}3342// Set and update the best block record3343// Set last block scanned as the last block processed as it may be different in case of a reorg.3344// Also save the best block locator because rescanning only updates it intermittently.3345walletInstance->SetLastBlockProcessed(*scan_res.last_scanned_height, scan_res.last_scanned_block);3346}3347}3349return true;3350}3352const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest, bool allow_change) const3353{3354const auto& address_book_it = m_address_book.find(dest);3355if (address_book_it == m_address_book.end()) return nullptr;3356if ((!allow_change) && address_book_it->second.IsChange()) {3357return nullptr;3358}3359return &address_book_it->second;3360}3362void CWallet::postInitProcess()3363{3364// Add wallet transactions that aren't already in a block to mempool3365// Do this here as mempool requires genesis block to be loaded3366ResubmitWalletTransactions(node::TxBroadcast::MEMPOOL_NO_BROADCAST, /*force=*/true);3368// Update wallet transactions with current mempool transactions.3369WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));3370}3372bool CWallet::BackupWallet(const std::string& strDest) const3373{3374WITH_LOCK(cs_wallet, WriteBestBlock());3375return GetDatabase().Backup(strDest);3376}3378int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const3379{3380AssertLockHeld(cs_wallet);3381if (auto* conf = wtx.state<TxStateConfirmed>()) {3382assert(conf->confirmed_block_height >= 0);3383return GetLastBlockHeight() - conf->confirmed_block_height + 1;3384} else if (auto* conf = wtx.state<TxStateBlockConflicted>()) {3385assert(conf->conflicting_block_height >= 0);3386return -1 * (GetLastBlockHeight() - conf->conflicting_block_height + 1);3387} else {3388return 0;3389}3390}3392int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const3393{3394AssertLockHeld(cs_wallet);3396if (!wtx.IsCoinBase()) {3397return 0;3398}3399int chain_depth = GetTxDepthInMainChain(wtx);3400assert(chain_depth >= 0); // coinbase tx should not be conflicted3401return std::max(0, (COINBASE_MATURITY+1) - chain_depth);3402}3404bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const3405{3406AssertLockHeld(cs_wallet);3408// note GetBlocksToMaturity is 0 for non-coinbase tx3409return GetTxBlocksToMaturity(wtx) > 0;3410}3412bool CWallet::IsLocked() const3413{3414if (!HasEncryptionKeys()) {3415return false;3416}3417LOCK(cs_wallet);3418return vMasterKey.empty();3419}3421bool CWallet::Lock()3422{3423if (!HasEncryptionKeys())3424return false;3426{3427LOCK2(m_relock_mutex, cs_wallet);3428if (!vMasterKey.empty()) {3429memory_cleanse(vMasterKey.data(), vMasterKey.size() * sizeof(decltype(vMasterKey)::value_type));3430vMasterKey.clear();3431}3432}3434NotifyStatusChanged(this);3435return true;3436}3438bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn)3439{3440{3441LOCK(cs_wallet);3442for (const auto& spk_man_pair : m_spk_managers) {3443if (!spk_man_pair.second->CheckDecryptionKey(vMasterKeyIn)) {3444return false;3445}3446}3447vMasterKey = vMasterKeyIn;3448}3449NotifyStatusChanged(this);3450return true;3451}3453std::set<ScriptPubKeyMan*> CWallet::GetActiveScriptPubKeyMans() const3454{3455std::set<ScriptPubKeyMan*> spk_mans;3456for (bool internal : {false, true}) {3457for (OutputType t : OUTPUT_TYPES) {3458auto spk_man = GetScriptPubKeyMan(t, internal);3459if (spk_man) {3460spk_mans.insert(spk_man);3461}3462}3463}3464return spk_mans;3465}3467bool CWallet::IsActiveScriptPubKeyMan(const ScriptPubKeyMan& spkm) const3468{3469for (const auto& [_, ext_spkm] : m_external_spk_managers) {3470if (ext_spkm == &spkm) return true;3471}3472for (const auto& [_, int_spkm] : m_internal_spk_managers) {3473if (int_spkm == &spkm) return true;3474}3475return false;3476}3478std::set<ScriptPubKeyMan*> CWallet::GetAllScriptPubKeyMans() const3479{3480std::set<ScriptPubKeyMan*> spk_mans;3481for (const auto& spk_man_pair : m_spk_managers) {3482spk_mans.insert(spk_man_pair.second.get());3483}3484return spk_mans;3485}3487ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool internal) const3488{3489const std::map<OutputType, ScriptPubKeyMan*>& spk_managers = internal ? m_internal_spk_managers : m_external_spk_managers;3490std::map<OutputType, ScriptPubKeyMan*>::const_iterator it = spk_managers.find(type);3491if (it == spk_managers.end()) {3492return nullptr;3493}3494return it->second;3495}3497std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const3498{3499std::set<ScriptPubKeyMan*> spk_mans;