From 9a1d73fdffa4f35e33bc187ac9b3afbba28b1950 Mon Sep 17 00:00:00 2001 From: John Moffett Date: Fri, 30 Dec 2022 16:09:56 -0500 Subject: [PATCH] Fix segfault when shutdown during wallet open If you open a wallet and send a shutdown signal during that process, the GUI will segfault due to some queued wallet events happening after the wallet controller is deleted. This is a minimal fix for those issues. --- src/qt/bitcoingui.cpp | 8 ++++++-- src/qt/sendcoinsdialog.cpp | 13 +++++++++---- src/qt/test/wallettests.cpp | 2 ++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a0731b337aa..47e4f9978df 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -680,6 +680,10 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller) GUIUtil::ExceptionSafeConnect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet); connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet); + connect(wallet_controller, &WalletController::destroyed, this, [this] { + // wallet_controller gets destroyed manually, but it leaves our member copy dangling + m_wallet_controller = nullptr; + }); auto activity = new LoadWalletsActivity(m_wallet_controller, this); activity->load(); @@ -692,7 +696,7 @@ WalletController* BitcoinGUI::getWalletController() void BitcoinGUI::addWallet(WalletModel* walletModel) { - if (!walletFrame) return; + if (!walletFrame || !m_wallet_controller) return; WalletView* wallet_view = new WalletView(walletModel, platformStyle, walletFrame); if (!walletFrame->addView(wallet_view)) return; @@ -742,7 +746,7 @@ void BitcoinGUI::removeWallet(WalletModel* walletModel) void BitcoinGUI::setCurrentWallet(WalletModel* wallet_model) { - if (!walletFrame) return; + if (!walletFrame || !m_wallet_controller) return; walletFrame->setCurrentWallet(wallet_model); for (int index = 0; index < m_wallet_selector->count(); ++index) { if (m_wallet_selector->itemData(index).value() == wallet_model) { diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 249e3f2101c..70f34038b6e 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -600,10 +600,15 @@ SendCoinsEntry *SendCoinsDialog::addEntry() entry->clear(); entry->setFocus(); ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint()); - qApp->processEvents(); - QScrollBar* bar = ui->scrollArea->verticalScrollBar(); - if(bar) - bar->setSliderPosition(bar->maximum()); + + // Scroll to the newly added entry on a QueuedConnection because Qt doesn't + // adjust the scroll area and scrollbar immediately when the widget is added. + // Invoking on a DirectConnection will only scroll to the second-to-last entry. + QMetaObject::invokeMethod(ui->scrollArea, [this] { + if (ui->scrollArea->verticalScrollBar()) { + ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum()); + } + }, Qt::QueuedConnection); updateTabsAndLabels(); return entry; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 59a59348906..34f5e78fc28 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -212,6 +212,8 @@ void TestGUI(interfaces::Node& node) QCOMPARE(transactionTableModel->rowCount({}), 105); uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, /*rbf=*/false); uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, /*rbf=*/true); + // Transaction table model updates on a QueuedConnection, so process events to ensure it's updated. + qApp->processEvents(); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid());