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.
1751if (!spk_man->IsHDEnabled()) return false;1752result = true;1753}1754return result;1755}1757bool CWallet::CanGetAddresses(bool internal) const1758{1759LOCK(cs_wallet);1760if (m_spk_managers.empty()) return false;1761for (OutputType t : OUTPUT_TYPES) {1762auto spk_man = GetScriptPubKeyMan(t, internal);1763if (spk_man && spk_man->CanGetAddresses(internal)) {1764return true;1765}1766}1767return false;1768}1770void CWallet::SetWalletFlag(uint64_t flags)1771{1772WalletBatch batch(GetDatabase());1773return SetWalletFlagWithDB(batch, flags);1774}1776void CWallet::SetWalletFlagWithDB(WalletBatch& batch, uint64_t flags)1777{1778LOCK(cs_wallet);1779m_wallet_flags |= flags;1780if (!batch.WriteWalletFlags(m_wallet_flags))1781throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");1782}1784void CWallet::UnsetWalletFlag(uint64_t flag)1785{1786WalletBatch batch(GetDatabase());1787UnsetWalletFlagWithDB(batch, flag);1788}1790void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)1791{1792LOCK(cs_wallet);1793m_wallet_flags &= ~flag;1794if (!batch.WriteWalletFlags(m_wallet_flags))1795throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");1796}1798void CWallet::UnsetBlankWalletFlag(WalletBatch& batch)1799{1800UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);1801}1803bool CWallet::IsWalletFlagSet(uint64_t flag) const1804{1805return (m_wallet_flags & flag);1806}1808bool CWallet::LoadWalletFlags(uint64_t flags)1809{1810LOCK(cs_wallet);1811if (((flags & KNOWN_WALLET_FLAGS) >> 32) ^ (flags >> 32)) {1812// contains unknown non-tolerable wallet flags1813return false;1814}1815m_wallet_flags = flags;1817return true;1818}1820void CWallet::InitWalletFlags(uint64_t flags)1821{1822LOCK(cs_wallet);1824// We should never be writing unknown non-tolerable wallet flags1825assert(((flags & KNOWN_WALLET_FLAGS) >> 32) == (flags >> 32));1826// This should only be used once, when creating a new wallet - so current flags are expected to be blank1827assert(m_wallet_flags == 0);1829if (!WalletBatch(GetDatabase()).WriteWalletFlags(flags)) {1830throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");1831}1833if (!LoadWalletFlags(flags)) assert(false);1834}1836uint64_t CWallet::GetWalletFlags() const1837{1838return m_wallet_flags;1839}1841void CWallet::MaybeUpdateBirthTime(int64_t time)1842{1843int64_t birthtime = m_birth_time.load();1844if (time < birthtime) {1845m_birth_time = time;1846}1847}1849/**1850* Scan active chain for relevant transactions after importing keys. This should1851* be called whenever new keys are added to the wallet, with the oldest key1852* creation time.1853*1854* @return Earliest timestamp that could be successfully scanned from. Timestamp1855* returned will be higher than startTime if relevant blocks could not be read.1856*/1857int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver)1858{1859// Find starting block. May be null if nCreateTime is greater than the1860// highest blockchain timestamp, in which case there is nothing that needs1861// to be scanned.1862int start_height = 0;1863uint256 start_block;1864bool start = chain().findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, FoundBlock().hash(start_block).height(start_height));1865WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - start_height + 1 : 0);1867if (start) {1868// TODO: this should take into account failure by ScanResult::USER_ABORT1869ScanResult result = ScanForWalletTransactions(start_block, start_height, /*max_height=*/{}, reserver, /*save_progress=*/false);1870if (result.status == ScanResult::FAILURE) {1871int64_t time_max;1872CHECK_NONFATAL(chain().findBlock(result.last_failed_block, FoundBlock().maxTime(time_max)));1873return time_max + TIMESTAMP_WINDOW + 1;1874}1875}1876return startTime;1877}1879/**1880* Scan the block chain (starting in start_block) for transactions1881* from or to us. If max_height is not set, the1882* mempool will be scanned as well.1883*1884* @param[in] start_block Scan starting block. If block is not on the active1885* chain, the scan will return SUCCESS immediately.1886* @param[in] start_height Height of start_block1887* @param[in] max_height Optional max scanning height. If unset there is1888* no maximum and scanning can continue to the tip1889*1890* @return ScanResult returning scan information and indicating success or1891* failure. Return status will be set to SUCCESS if scan was1892* successful. FAILURE if a complete rescan was not possible (due to1893* pruning or corruption). USER_ABORT if the rescan was aborted before1894* it could complete.1895*1896* @pre Caller needs to make sure start_block (and the optional stop_block) are on1897* the main chain after to the addition of any new keys you want to detect1898* transactions for.1899*/1900CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, const bool save_progress)1901{1902constexpr auto INTERVAL_TIME{60s};1903auto current_time{reserver.now()};1904auto start_time{reserver.now()};1906assert(reserver.isReserved());1908uint256 block_hash = start_block;1909ScanResult result;1911std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter;1912if (chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);1914WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(),1915fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks");1917ShowProgress(strprintf("[%s] %s", DisplayName(), _("Rescanning…")), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)1918uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());1919uint256 end_hash = tip_hash;1920if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));1921double progress_begin = chain().guessVerificationProgress(block_hash);1922double progress_end = chain().guessVerificationProgress(end_hash);1923double progress_current = progress_begin;1924int block_height = start_height;1925while (!fAbortRescan && !chain().shutdownRequested()) {1926if (progress_end - progress_begin > 0.0) {1927m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin);1928} else { // avoid divide-by-zero for single block scan range (i.e. start and stop hashes are equal)1929m_scanning_progress = 0;1930}1931if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) {1932ShowProgress(strprintf("[%s] %s", DisplayName(), _("Rescanning…")), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));1933}1935bool next_interval = reserver.now() >= current_time + INTERVAL_TIME;1936if (next_interval) {1937current_time = reserver.now();1938WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current);1939}1941bool fetch_block{true};1942if (fast_rescan_filter) {1943fast_rescan_filter->UpdateIfNeeded();1944auto matches_block{fast_rescan_filter->MatchesBlock(block_hash)};1945if (matches_block.has_value()) {1946if (*matches_block) {1947LogDebug(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (filter matched)\n", block_height, block_hash.ToString());1948} else {1949result.last_scanned_block = block_hash;1950result.last_scanned_height = block_height;1951fetch_block = false;1952}1953} else {1954LogDebug(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (WARNING: block filter not found!)\n", block_height, block_hash.ToString());1955}1956}1958// Find next block separately from reading data above, because reading1959// is slow and there might be a reorg while it is read.1960bool block_still_active = false;1961bool next_block = false;1962uint256 next_block_hash;1963chain().findBlock(block_hash, FoundBlock().inActiveChain(block_still_active).nextBlock(FoundBlock().inActiveChain(next_block).hash(next_block_hash)));1965if (fetch_block) {1966// Read block data and locator if needed (the locator is usually null unless we need to save progress)1967CBlock block;1968CBlockLocator loc;1969// Find block1970FoundBlock found_block{FoundBlock().data(block)};1971if (save_progress && next_interval) found_block.locator(loc);1972chain().findBlock(block_hash, found_block);1974if (!block.IsNull()) {1975LOCK(cs_wallet);1976if (!block_still_active) {1977// Abort scan if current block is no longer active, to prevent1978// marking transactions as coming from the wrong block.1979result.last_failed_block = block_hash;1980result.status = ScanResult::FAILURE;1981break;1982}1983for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {1984SyncTransaction(block.vtx[posInBlock], TxStateConfirmed{block_hash, block_height, static_cast<int>(posInBlock)}, /*rescanning_old_block=*/true);1985}1986// scan succeeded, record block as most recent successfully scanned1987result.last_scanned_block = block_hash;1988result.last_scanned_height = block_height;1990if (!loc.IsNull()) {1991WalletLogPrintf("Saving scan progress %d.\n", block_height);1992WalletBatch batch(GetDatabase());1993batch.WriteBestBlock(loc);1994}1995} else {1996// could not scan block, keep scanning but record this block as the most recent failure1997result.last_failed_block = block_hash;1998result.status = ScanResult::FAILURE;1999}2000}