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) 2019-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/walletcontroller.h>7#include <qt/askpassphrasedialog.h>8#include <qt/clientmodel.h>9#include <qt/createwalletdialog.h>10#include <qt/guiconstants.h>11#include <qt/guiutil.h>12#include <qt/walletmodel.h>14#include <external_signer.h>15#include <interfaces/handler.h>16#include <interfaces/node.h>17#include <util/string.h>18#include <util/threadnames.h>19#include <util/translation.h>20#include <wallet/wallet.h>22#include <algorithm>23#include <chrono>25#include <QApplication>26#include <QCheckBox>27#include <QMessageBox>28#include <QMetaObject>29#include <QMutexLocker>30#include <QThread>31#include <QTimer>32#include <QWindow>34using util::Join;35using wallet::WALLET_FLAG_BLANK_WALLET;36using wallet::WALLET_FLAG_DESCRIPTORS;37using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS;38using wallet::WALLET_FLAG_EXTERNAL_SIGNER;40WalletController::WalletController(ClientModel& client_model, const PlatformStyle* platform_style, QObject* parent)41: QObject(parent)42, m_activity_thread(new QThread(this))43, m_activity_worker(new QObject)44, m_client_model(client_model)45, m_node(client_model.node())46, m_platform_style(platform_style)47, m_options_model(client_model.getOptionsModel())48{49m_handler_load_wallet = m_node.walletLoader().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {50getOrCreateWallet(std::move(wallet));51});53m_activity_worker->moveToThread(m_activity_thread);54m_activity_thread->start();55QTimer::singleShot(0, m_activity_worker, []() {56util::ThreadRename("qt-walletctrl");57});58}60// Not using the default destructor because not all member types definitions are61// available in the header, just forward declared.62WalletController::~WalletController()63{64m_activity_thread->quit();65m_activity_thread->wait();66delete m_activity_worker;67}69std::map<std::string, std::pair<bool, std::string>> WalletController::listWalletDir() const70{71QMutexLocker locker(&m_mutex);72std::map<std::string, std::pair<bool, std::string>> wallets;73for (const auto& [name, format] : m_node.walletLoader().listWalletDir()) {74wallets[name] = std::make_pair(false, format);75}76for (WalletModel* wallet_model : m_wallets) {77auto it = wallets.find(wallet_model->wallet().getWalletName());78if (it != wallets.end()) it->second.first = true;79}80return wallets;81}83void WalletController::removeWallet(WalletModel* wallet_model)84{85// Once the wallet is successfully removed from the node, the model will emit the 'WalletModel::unload' signal.86// This signal is already connected and will complete the removal of the view from the GUI.87// Look at 'WalletController::getOrCreateWallet' for the signal connection.88wallet_model->wallet().remove();89}91void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)92{93QMessageBox box(parent);94box.setWindowTitle(tr("Close wallet"));95box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));96box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));97box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);98box.setDefaultButton(QMessageBox::Yes);99if (box.exec() != QMessageBox::Yes) return;101removeWallet(wallet_model);102}104void WalletController::closeAllWallets(QWidget* parent)105{106QMessageBox::StandardButton button = QMessageBox::question(parent, tr("Close all wallets"),107tr("Are you sure you wish to close all wallets?"),108QMessageBox::Yes|QMessageBox::Cancel,109QMessageBox::Yes);110if (button != QMessageBox::Yes) return;112QMutexLocker locker(&m_mutex);113for (WalletModel* wallet_model : m_wallets) {114removeWallet(wallet_model);115}116}118WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)119{120QMutexLocker locker(&m_mutex);122// Return model instance if exists.123if (!m_wallets.empty()) {124std::string name = wallet->getWalletName();125for (WalletModel* wallet_model : m_wallets) {126if (wallet_model->wallet().getWalletName() == name) {127return wallet_model;128}129}130}132// Instantiate model and register it.133WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style,134nullptr /* required for the following moveToThread() call */);136// Move WalletModel object to the thread that created the WalletController137// object (GUI main thread), instead of the current thread, which could be138// an outside wallet thread or RPC thread sending a LoadWallet notification.139// This ensures queued signals sent to the WalletModel object will be140// handled on the GUI event loop.141wallet_model->moveToThread(thread());142// setParent(parent) must be called in the thread which created the parent object. More details in #18948.143QMetaObject::invokeMethod(this, [wallet_model, this] {144wallet_model->setParent(this);145}, GUIUtil::blockingGUIThreadConnection());147m_wallets.push_back(wallet_model);149// WalletModel::startPollBalance needs to be called in a thread managed by150// Qt because of startTimer. Considering the current thread can be a RPC151// thread, better delegate the calling to Qt with Qt::AutoConnection.152const bool called = QMetaObject::invokeMethod(wallet_model, "startPollBalance");153assert(called);155connect(wallet_model, &WalletModel::unload, this, [this, wallet_model] {156// Defer removeAndDeleteWallet when no modal widget is actively waiting for an action.157// TODO: remove this workaround by removing usage of QDialog::exec.158QWidget* active_dialog = QApplication::activeModalWidget();159if (active_dialog && dynamic_cast<QProgressDialog*>(active_dialog) == nullptr) {160connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {161if (!QApplication::activeModalWidget()) {162removeAndDeleteWallet(wallet_model);163}164}, Qt::QueuedConnection);165} else {166removeAndDeleteWallet(wallet_model);167}168}, Qt::QueuedConnection);170// Re-emit coinsSent signal from wallet model.171connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);173Q_EMIT walletAdded(wallet_model);175return wallet_model;176}178void WalletController::removeAndDeleteWallet(WalletModel* wallet_model)179{180// Unregister wallet model.181{182QMutexLocker locker(&m_mutex);183m_wallets.erase(std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));184}185Q_EMIT walletRemoved(wallet_model);186// Currently this can trigger the unload since the model can hold the last187// CWallet shared pointer.188delete wallet_model;189}191WalletControllerActivity::WalletControllerActivity(WalletController* wallet_controller, QWidget* parent_widget)192: QObject(wallet_controller)193, m_wallet_controller(wallet_controller)194, m_parent_widget(parent_widget)195{196connect(this, &WalletControllerActivity::finished, this, &QObject::deleteLater);197}199void WalletControllerActivity::showProgressDialog(const QString& title_text, const QString& label_text, bool show_minimized)200{201auto progress_dialog = new QProgressDialog(m_parent_widget);202progress_dialog->setAttribute(Qt::WA_DeleteOnClose);203connect(this, &WalletControllerActivity::finished, progress_dialog, &QWidget::close);205progress_dialog->setWindowTitle(title_text);206progress_dialog->setLabelText(label_text);207progress_dialog->setRange(0, 0);208progress_dialog->setCancelButton(nullptr);209progress_dialog->setWindowModality(Qt::ApplicationModal);210GUIUtil::PolishProgressDialog(progress_dialog);211// The setValue call forces QProgressDialog to start the internal duration estimation.212// See details in https://bugreports.qt.io/browse/QTBUG-47042.213progress_dialog->setValue(0);214// When requested, launch dialog minimized215if (show_minimized) progress_dialog->showMinimized();216}218CreateWalletActivity::CreateWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)219: WalletControllerActivity(wallet_controller, parent_widget)220{221m_passphrase.reserve(MAX_PASSPHRASE_SIZE);222}224CreateWalletActivity::~CreateWalletActivity()225{226delete m_create_wallet_dialog;227delete m_passphrase_dialog;228}230void CreateWalletActivity::askPassphrase()231{232m_passphrase_dialog = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, m_parent_widget, &m_passphrase);233m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);234m_passphrase_dialog->show();236connect(m_passphrase_dialog, &QObject::destroyed, [this] {237m_passphrase_dialog = nullptr;238});239connect(m_passphrase_dialog, &QDialog::accepted, [this] {240createWallet();241});242connect(m_passphrase_dialog, &QDialog::rejected, [this] {243Q_EMIT finished();244});245}247void CreateWalletActivity::createWallet()248{249showProgressDialog(250//: Title of window indicating the progress of creation of a new wallet.
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.