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.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <qt/walletmodel.h>7#include <qt/addresstablemodel.h>8#include <qt/clientmodel.h>9#include <qt/guiconstants.h>10#include <qt/guiutil.h>11#include <qt/optionsmodel.h>12#include <qt/paymentserver.h>13#include <qt/recentrequeststablemodel.h>14#include <qt/sendcoinsdialog.h>15#include <qt/transactiontablemodel.h>17#include <common/args.h>18#include <interfaces/handler.h>19#include <interfaces/node.h>20#include <key_io.h>21#include <node/interface_ui.h>22#include <node/types.h>23#include <psbt.h>24#include <util/translation.h>25#include <wallet/coincontrol.h>26#include <wallet/types.h>27#include <wallet/wallet.h>29#include <cstdint>30#include <functional>31#include <memory>32#include <vector>34#include <QDebug>35#include <QMessageBox>36#include <QSet>37#include <QTimer>39using wallet::CCoinControl;40using wallet::CRecipient;41using wallet::DEFAULT_DISABLE_WALLET;43WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel& client_model, const PlatformStyle *platformStyle, QObject *parent) :44QObject(parent),45m_wallet(std::move(wallet)),46m_client_model(&client_model),47m_node(client_model.node()),48optionsModel(client_model.getOptionsModel()),49timer(new QTimer(this))50{51addressTableModel = new AddressTableModel(this);52transactionTableModel = new TransactionTableModel(platformStyle, this);53recentRequestsTableModel = new RecentRequestsTableModel(this);55subscribeToCoreSignals();56}58WalletModel::~WalletModel()59{60unsubscribeFromCoreSignals();61}63void WalletModel::startPollBalance()64{65// Update the cached balance right away, so every view can make use of it,66// so them don't need to waste resources recalculating it.67pollBalanceChanged();69// This timer will be fired repeatedly to update the balance70// Since the QTimer::timeout is a private signal, it cannot be used71// in the GUIUtil::ExceptionSafeConnect directly.72connect(timer, &QTimer::timeout, this, &WalletModel::timerTimeout);73GUIUtil::ExceptionSafeConnect(this, &WalletModel::timerTimeout, this, &WalletModel::pollBalanceChanged);74timer->start(MODEL_UPDATE_DELAY);75}77void WalletModel::setClientModel(ClientModel* client_model)78{79m_client_model = client_model;80if (!m_client_model) timer->stop();81}83void WalletModel::updateStatus()84{85EncryptionStatus newEncryptionStatus = getEncryptionStatus();87if(cachedEncryptionStatus != newEncryptionStatus) {88Q_EMIT encryptionStatusChanged();89}90}92void WalletModel::pollBalanceChanged()93{94// Avoid recomputing wallet balances unless a TransactionChanged or95// BlockTip notification was received.96if (!fForceCheckBalanceChanged && m_cached_last_update_tip == getLastBlockProcessed()) return;98// Try to get balances and return early if locks can't be acquired. This99// avoids the GUI from getting stuck on periodical polls if the core is100// holding the locks for a longer time - for example, during a wallet101// rescan.102interfaces::WalletBalances new_balances;103uint256 block_hash;104if (!m_wallet->tryGetBalances(new_balances, block_hash)) {105return;106}108if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {109fForceCheckBalanceChanged = false;111// Balance and number of transactions might have changed112m_cached_last_update_tip = block_hash;114checkBalanceChanged(new_balances);115if(transactionTableModel)116transactionTableModel->updateConfirmations();117}118}120void WalletModel::checkBalanceChanged(const interfaces::WalletBalances& new_balances)121{122if (new_balances.balanceChanged(m_cached_balances)) {123m_cached_balances = new_balances;124Q_EMIT balanceChanged(new_balances);125}126}128interfaces::WalletBalances WalletModel::getCachedBalance() const129{130return m_cached_balances;131}133void WalletModel::updateTransaction()134{135// Balance and number of transactions might have changed136fForceCheckBalanceChanged = true;137}139void WalletModel::updateAddressBook(const QString &address, const QString &label,140bool isMine, wallet::AddressPurpose purpose, int status)141{142if(addressTableModel)143addressTableModel->updateEntry(address, label, isMine, purpose, status);144}146bool WalletModel::validateAddress(const QString& address) const147{148return IsValidDestinationString(address.toStdString());149}151WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)152{153transaction.getWtx() = nullptr; // reset tx output155CAmount total = 0;156bool fSubtractFeeFromAmount = false;157QList<SendCoinsRecipient> recipients = transaction.getRecipients();158std::vector<CRecipient> vecSend;160if(recipients.empty())161{162return OK;163}165QSet<QString> setAddress; // Used to detect duplicates166int nAddresses = 0;168// Pre-check input data for validity169for (const SendCoinsRecipient &rcp : recipients)170{171if (rcp.fSubtractFeeFromAmount)172fSubtractFeeFromAmount = true;173{ // User-entered bitcoin address / amount:174if(!validateAddress(rcp.address))175{176return InvalidAddress;177}178if(rcp.amount <= 0)179{180return InvalidAmount;181}182setAddress.insert(rcp.address);183++nAddresses;185vecSend.emplace_back(CRecipient{DecodeDestination(rcp.address.toStdString()), rcp.amount, rcp.fSubtractFeeFromAmount});187total += rcp.amount;188}189}190if(setAddress.size() != nAddresses)191{192return DuplicateAddress;193}195// If no coin was manually selected, use the cached balance196// Future: can merge this call with 'createTransaction'.197CAmount nBalance = getAvailableBalance(&coinControl);199if(total > nBalance)200{201return AmountExceedsBalance;202}204try {205auto& newTx = transaction.getWtx();206const auto& res = m_wallet->createTransaction(vecSend, coinControl, /*sign=*/!wallet().privateKeysDisabled(), /*change_pos=*/std::nullopt);207if (!res) {208Q_EMIT message(tr("Send Coins"), QString::fromStdString(util::ErrorString(res).translated),209CClientUIInterface::MSG_ERROR);210return TransactionCreationFailed;211}213newTx = res->tx;214CAmount nFeeRequired = res->fee;215transaction.setTransactionFee(nFeeRequired);216if (fSubtractFeeFromAmount && newTx) {217transaction.reassignAmounts(static_cast<int>(res->change_pos.value_or(-1)));218}220// Reject absurdly high fee. (This can never happen because the221// wallet never creates transactions with fee greater than222// m_default_max_tx_fee. This merely a belt-and-suspenders check).223if (nFeeRequired > m_wallet->getDefaultMaxTxFee()) {224return AbsurdFee;225}226} catch (const std::runtime_error& err) {227// Something unexpected happened, instruct user to report this bug.228Q_EMIT message(tr("Send Coins"), QString::fromStdString(err.what()),229CClientUIInterface::MSG_ERROR);230return TransactionCreationFailed;231}233return SendCoinsReturn(OK);234}236void WalletModel::sendCoins(WalletModelTransaction& transaction)237{238QByteArray transaction_array; /* store serialized transaction */240{241std::vector<std::string> messages;242for (const SendCoinsRecipient &rcp : transaction.getRecipients())243{244if (!rcp.message.isEmpty()) { // Message from normal bitcoin:URI (bitcoin:123...?message=example)245messages.emplace_back(rcp.message.toStdString());246}247}249auto& newTx = transaction.getWtx();250wallet().commitTransaction(newTx, messages);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.