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.
501}502} else {503// The directory doesn't exist, create it504if (!TryCreateDirectories(wallet_path)) {505error = Untranslated(strprintf("Failed to restore database path '%s'.", fs::PathToString(wallet_path)));506status = DatabaseStatus::FAILED_ALREADY_EXISTS;507return nullptr;508}509created_parent_dir = true;510}512fs::copy_file(backup_file, wallet_file, fs::copy_options::none);513wallet_file_copied = true;515if (load_after_restore) {516wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);517}518} catch (const std::exception& e) {519assert(!wallet);520if (!error.empty()) error += Untranslated("\n");521error += Untranslated(strprintf("Unexpected exception: %s", e.what()));522}524// Remove created wallet path only when loading fails525if (load_after_restore && !wallet) {526if (wallet_file_copied) fs::remove(wallet_file);527// Clean up the parent directory if we created it during restoration.528// As we have created it, it must be empty after deleting the wallet file.529if (created_parent_dir) {530Assume(fs::is_empty(wallet_path));531fs::remove(wallet_path);532}533}535return wallet;536}538/** @defgroup mapWallet539*540* @{541*/543const CWalletTx* CWallet::GetWalletTx(const Txid& hash) const544{545AssertLockHeld(cs_wallet);546const auto it = mapWallet.find(hash);547if (it == mapWallet.end())548return nullptr;549return &(it->second);550}552void CWallet::UpgradeDescriptorCache()553{554if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {555return;556}558for (ScriptPubKeyMan* spkm : GetAllScriptPubKeyMans()) {559DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm);560desc_spkm->UpgradeDescriptorCache();561}562SetWalletFlag(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);563}565/* Given a wallet passphrase string and an unencrypted master key, determine the proper key566* derivation parameters (should take at least 100ms) and encrypt the master key. */567static bool EncryptMasterKey(const SecureString& wallet_passphrase, const CKeyingMaterial& plain_master_key, CMasterKey& master_key)568{569constexpr MillisecondsDouble target_time{100};570CCrypter crypter;571CMasterKey updated_master_key{master_key};573// Get the weighted average of iterations we can do in 100ms over 2 runs.574for (int i = 0; i < 2; i++){575auto start_time{NodeClock::now()};576const bool key_set{crypter.SetKeyFromPassphrase(wallet_passphrase, updated_master_key.vchSalt, updated_master_key.nDeriveIterations, updated_master_key.nDerivationMethod)};577auto elapsed_time{NodeClock::now() - start_time};578if (!key_set) {579return false;580}582if (elapsed_time <= 0s) {583// We are probably in a test with a mocked clock.584updated_master_key.nDeriveIterations = CMasterKey::DEFAULT_DERIVE_ITERATIONS;585break;586}588// target_iterations : elapsed_iterations :: target_time : elapsed_time589const double target_iterations{updated_master_key.nDeriveIterations * target_time / elapsed_time};590if (target_iterations < 1 || target_iterations > std::numeric_limits<unsigned int>::max()) {591return false;592}593// Get the weighted average with previous runs. Use 64-bit math so the594// sum cannot wrap; the average of two unsigned int values fits in one.595updated_master_key.nDeriveIterations = (uint64_t{updated_master_key.nDeriveIterations} * i + static_cast<unsigned int>(target_iterations)) / (i + 1);596}598if (updated_master_key.nDeriveIterations < CMasterKey::DEFAULT_DERIVE_ITERATIONS) {599updated_master_key.nDeriveIterations = CMasterKey::DEFAULT_DERIVE_ITERATIONS;600}602if (!crypter.SetKeyFromPassphrase(wallet_passphrase, updated_master_key.vchSalt, updated_master_key.nDeriveIterations, updated_master_key.nDerivationMethod)) {603return false;604}605if (!crypter.Encrypt(plain_master_key, updated_master_key.vchCryptedKey)) {606return false;607}609master_key = std::move(updated_master_key);610return true;611}613static bool DecryptMasterKey(const SecureString& wallet_passphrase, const CMasterKey& master_key, CKeyingMaterial& plain_master_key)614{615CCrypter crypter;616if (!crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod)) {617return false;618}619if (!crypter.Decrypt(master_key.vchCryptedKey, plain_master_key)) {620return false;621}623return true;624}626bool CWallet::Unlock(const SecureString& strWalletPassphrase)627{628CKeyingMaterial plain_master_key;630{631LOCK(cs_wallet);632for (const auto& [_, master_key] : mapMasterKeys)633{634if (!DecryptMasterKey(strWalletPassphrase, master_key, plain_master_key)) {635continue; // try another master key636}637if (Unlock(plain_master_key)) {638// Now that we've unlocked, upgrade the descriptor cache639UpgradeDescriptorCache();640return true;641}642}643}644return false;645}647bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)648{649bool fWasLocked = IsLocked();651{652LOCK2(m_relock_mutex, cs_wallet);653Lock();655CKeyingMaterial plain_master_key;656for (auto& [master_key_id, master_key] : mapMasterKeys)657{658if (!DecryptMasterKey(strOldWalletPassphrase, master_key, plain_master_key)) {659return false;660}661if (Unlock(plain_master_key))662{663if (!EncryptMasterKey(strNewWalletPassphrase, plain_master_key, master_key)) {664return false;665}666WalletLogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", master_key.nDeriveIterations);668WalletBatch(GetDatabase()).WriteMasterKey(master_key_id, master_key);669if (fWasLocked)670Lock();671return true;672}673}674}676return false;677}679void CWallet::SetLastBlockProcessedInMem(int block_height, uint256 block_hash)680{681AssertLockHeld(cs_wallet);683m_last_block_processed = block_hash;684m_last_block_processed_height = block_height;685}687void CWallet::SetLastBlockProcessed(int block_height, uint256 block_hash)688{689AssertLockHeld(cs_wallet);691SetLastBlockProcessedInMem(block_height, block_hash);692WriteBestBlock();693}695std::set<Txid> CWallet::GetConflicts(const Txid& txid) const696{697std::set<Txid> result;698AssertLockHeld(cs_wallet);700const auto it = mapWallet.find(txid);701if (it == mapWallet.end())702return result;703const CWalletTx& wtx = it->second;705std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;707for (const CTxIn& txin : wtx.GetTx()->vin)708{709if (mapTxSpends.count(txin.prevout) <= 1)710continue; // No conflict if zero or one spends711range = mapTxSpends.equal_range(txin.prevout);712for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it)713result.insert(_it->second);714}715return result;716}718bool CWallet::HasWalletSpend(const CTransactionRef& tx) const719{720AssertLockHeld(cs_wallet);721const Txid& txid = tx->GetHash();722for (unsigned int i = 0; i < tx->vout.size(); ++i) {723if (IsSpent(COutPoint(txid, i))) {724return true;725}726}727return false;728}730void CWallet::Close()731{732GetDatabase().Close();733}735std::set<CWalletTx*, WalletTxOrderComparator> CWallet::GetMalleatedVariants(const CWalletTx& wtx)736{737AssertLockHeld(cs_wallet);738std::set<CWalletTx*, WalletTxOrderComparator> txs;740// Coinbases cannot be malleated741if (wtx.IsCoinBase()) return txs;743// Only transactions that have non-witness inputs can be malleated744if (std::ranges::none_of(wtx.GetTx()->vin, [](const CTxIn& in) { return in.scriptWitness.IsNull(); })) {745return txs;746}748// All variants spend wtx's first input, so a single lookup finds every candidate749bool found_self = false;750const auto [begin, end] = mapTxSpends.equal_range(wtx.GetTx()->vin.front().prevout);