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.
2251}2253std::optional<PrecomputedTransactionData> txdata_res = PrecomputePSBTData(psbtx);2254if (!txdata_res) {2255return PSBTError::INVALID_TX;2256}2257const PrecomputedTransactionData& txdata = *txdata_res;2259// Fill in information from ScriptPubKeyMans2260for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {2261int n_signed_this_spkm = 0;2262const auto error{spk_man->FillPSBT(psbtx, txdata, options, &n_signed_this_spkm)};2263if (error) {2264return error;2265}2267if (n_signed) {2268(*n_signed) += n_signed_this_spkm;2269}2270}2272RemoveUnnecessaryTransactions(psbtx);2274// Complete if every input is now signed2275complete = true;2276for (size_t i = 0; i < psbtx.inputs.size(); ++i) {2277complete &= PSBTInputSignedAndVerified(psbtx, i, &txdata);2278}2280return {};2281}2283SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const2284{2285SignatureData sigdata;2286CScript script_pub_key = GetScriptForDestination(pkhash);2287for (const auto& spk_man_pair : m_spk_managers) {2288if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {2289LOCK(cs_wallet); // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order2290return spk_man_pair.second->SignMessage(message, pkhash, str_sig);2291}2292}2293return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;2294}2296OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const2297{2298// If -changetype is specified, always use that change type.2299if (change_type) {2300return *change_type;2301}2303// if m_default_address_type is legacy, use legacy address as change.2304if (m_default_address_type == OutputType::LEGACY) {2305return OutputType::LEGACY;2306}2308bool any_tr{false};2309bool any_wpkh{false};2310bool any_sh{false};2311bool any_pkh{false};2313for (const auto& recipient : vecSend) {2314if (std::get_if<WitnessV1Taproot>(&recipient.dest)) {2315any_tr = true;2316} else if (std::get_if<WitnessV0KeyHash>(&recipient.dest)) {2317any_wpkh = true;2318} else if (std::get_if<ScriptHash>(&recipient.dest)) {2319any_sh = true;2320} else if (std::get_if<PKHash>(&recipient.dest)) {2321any_pkh = true;2322}2323}2325const bool has_bech32m_spkman(GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/true));2326if (has_bech32m_spkman && any_tr) {2327// Currently tr is the only type supported by the BECH32M spkman2328return OutputType::BECH32M;2329}2330const bool has_bech32_spkman(GetScriptPubKeyMan(OutputType::BECH32, /*internal=*/true));2331if (has_bech32_spkman && any_wpkh) {2332// Currently wpkh is the only type supported by the BECH32 spkman2333return OutputType::BECH32;2334}2335const bool has_p2sh_segwit_spkman(GetScriptPubKeyMan(OutputType::P2SH_SEGWIT, /*internal=*/true));2336if (has_p2sh_segwit_spkman && any_sh) {2337// Currently sh_wpkh is the only type supported by the P2SH_SEGWIT spkman2338// As of 2021 about 80% of all SH are wrapping WPKH, so use that2339return OutputType::P2SH_SEGWIT;2340}2341const bool has_legacy_spkman(GetScriptPubKeyMan(OutputType::LEGACY, /*internal=*/true));2342if (has_legacy_spkman && any_pkh) {2343// Currently pkh is the only type supported by the LEGACY spkman2344return OutputType::LEGACY;2345}2347if (has_bech32m_spkman) {2348return OutputType::BECH32M;2349}2350if (has_bech32_spkman) {2351return OutputType::BECH32;2352}2353// else use m_default_address_type for change2354return m_default_address_type;2355}2357void CWallet::CommitTransaction(2358CTransactionRef tx,2359std::optional<Txid> replaces_txid,2360std::optional<std::string> comment,2361std::optional<std::string> comment_to,2362const std::vector<std::string>& messages,2363const std::vector<std::string>& payment_requests2364)2365{2366LOCK(cs_wallet);2367WalletLogPrintf("CommitTransaction:\n%s\n", util::RemoveSuffixView(tx->ToString(), "\n"));2369// Add tx to wallet, because if it has change it's also ours,2370// otherwise just for transaction history.2371CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {2372if (replaces_txid) wtx.m_replaces_txid = replaces_txid;2373if (comment) wtx.m_comment = comment;2374if (comment_to) wtx.m_comment_to = comment_to;2375if (!messages.empty()) wtx.m_messages = messages;2376if (!payment_requests.empty()) wtx.m_payment_requests = payment_requests;2377return true;2378});2380// wtx can only be null if the db write failed.2381if (!wtx) {2382throw std::runtime_error(std::string(__func__) + ": Wallet db error, transaction commit failed");2383}2385// Notify that old coins are spent2386for (const CTxIn& txin : tx->vin) {2387CWalletTx &coin = mapWallet.at(txin.prevout.hash);2388coin.MarkDirty();2389NotifyTransactionChanged(coin.GetHash(), CT_UPDATED);2390}2392if (!fBroadcastTransactions) {2393// Don't submit tx to the mempool2394return;2395}2397std::string err_string;2398if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL)) {2399WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);2400// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.2401}2402}2404DBErrors CWallet::PopulateWalletFromDB(bilingual_str& error, std::vector<bilingual_str>& warnings)2405{2406LOCK(cs_wallet);2408Assert(m_spk_managers.empty());2409Assert(m_wallet_flags == 0);2410DBErrors nLoadWalletRet = WalletBatch(GetDatabase()).LoadWallet(this);2412if (m_spk_managers.empty()) {2413assert(m_external_spk_managers.empty());2414assert(m_internal_spk_managers.empty());2415}2417const auto wallet_file = m_database->Filename();2418switch (nLoadWalletRet) {2419case DBErrors::LOAD_OK:2420break;2421case DBErrors::NONCRITICAL_ERROR:2422warnings.push_back(strprintf(_("Error reading %s! All keys read correctly, but transaction data"2423" or address metadata may be missing or incorrect."),2424wallet_file));2425break;2426case DBErrors::NEED_RESCAN:2427warnings.push_back(strprintf(_("Error reading %s! Transaction data may be missing or incorrect."2428" Rescanning wallet."), wallet_file));2429break;2430case DBErrors::CORRUPT:2431error = strprintf(_("Error loading %s: Wallet corrupted"), wallet_file);2432break;2433case DBErrors::TOO_NEW:2434error = strprintf(_("Error loading %s: Wallet requires newer version of %s"), wallet_file, CLIENT_NAME);2435break;2436case DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED:2437error = strprintf(_("Error loading %s: External signer wallet being loaded without external signer support compiled"), wallet_file);2438break;2439case DBErrors::UNKNOWN_DESCRIPTOR:2440error = strprintf(_("Unrecognized descriptor found. Loading wallet %s\n\n"2441"The wallet might have been created on a newer version.\n"2442"Please try running the latest software version.\n"), wallet_file);2443break;2444case DBErrors::UNEXPECTED_LEGACY_ENTRY:2445error = strprintf(_("Unexpected legacy entry in descriptor wallet found. Loading wallet %s\n\n"2446"The wallet might have been tampered with or created with malicious intent.\n"), wallet_file);2447break;2448case DBErrors::LEGACY_WALLET:2449error = strprintf(_("Error loading %s: Wallet is a legacy wallet. Please migrate to a descriptor wallet using the migration tool (migratewallet RPC)."), wallet_file);2450break;2451case DBErrors::LOAD_FAIL:2452error = strprintf(_("Error loading %s"), wallet_file);2453break;2454} // no default case, so the compiler can warn about missing cases2455return nLoadWalletRet;2456}2458util::Result<void> CWallet::RemoveTxs(std::vector<Txid>& txs_to_remove)2459{2460AssertLockHeld(cs_wallet);2461bilingual_str str_err; // future: make RunWithinTxn return a util::Result2462bool was_txn_committed = RunWithinTxn(GetDatabase(), /*process_desc=*/"remove transactions", [&](WalletBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {2463util::Result<void> result{RemoveTxs(batch, txs_to_remove)};2464if (!result) str_err = util::ErrorString(result);2465return result.has_value();2466});2467if (!str_err.empty()) return util::Error{str_err};2468if (!was_txn_committed) return util::Error{_("Error starting/committing db txn for wallet transactions removal process")};2469return {}; // all good2470}2472util::Result<void> CWallet::RemoveTxs(WalletBatch& batch, std::vector<Txid>& txs_to_remove)2473{2474AssertLockHeld(cs_wallet);2475if (!batch.HasActiveTxn()) return util::Error{strprintf(_("The transactions removal process can only be executed within a db txn"))};2477// Check for transaction existence and remove entries from disk2478std::vector<decltype(mapWallet)::const_iterator> erased_txs;2479bilingual_str str_err;2480for (const Txid& hash : txs_to_remove) {2481auto it_wtx = mapWallet.find(hash);2482if (it_wtx == mapWallet.end()) {2483return util::Error{strprintf(_("Transaction %s does not belong to this wallet"), hash.GetHex())};2484}2485if (!batch.EraseTx(hash)) {2486return util::Error{strprintf(_("Failure removing transaction: %s"), hash.GetHex())};2487}2488erased_txs.emplace_back(it_wtx);2489}2491// Register callback to update the memory state only when the db txn is actually dumped to disk2492batch.RegisterTxnListener({.on_commit=[&, erased_txs]() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {2493// Update the in-memory state and notify upper layers about the removals2494for (const auto& it : erased_txs) {2495const Txid hash{it->first};2496wtxOrdered.erase(it->second.m_it_wtxOrdered);2497for (const auto& txin : it->second.GetTx()->vin) {2498auto range = mapTxSpends.equal_range(txin.prevout);2499for (auto iter = range.first; iter != range.second; ++iter) {2500if (iter->second == hash) {