Skip to content

Commit 6d4889a

Browse files
committed
Merge #598: Avoid recalculating the wallet balance - use model cache
4584d30 GUI: remove now unneeded 'm_balances' field from overviewpage (furszy) 050e8b1 GUI: 'getAvailableBalance', use cached balance if the user did not select UTXO manually (furszy) 96e3264 GUI: use cached balance in overviewpage and sendcoinsdialog (furszy) 321335b GUI: add getter for WalletModel::m_cached_balances field (furszy) e62958d GUI: sendCoinsDialog, remove duplicate wallet().getBalances() call (furszy) Pull request description: As per the title says, we are recalculating the entire wallet balance on different situations calling to `wallet().getBalances()`, when should instead make use of the wallet model cached balance. This has the benefits of (1) not spending resources calculating a balance that we already have cached, and (2) avoid blocking the main thread for a long time, in case of big wallets, walking through the entire wallet's tx map more than what it's really needed. Changes: 1) Fix: `SendCoinsDialog` was calling `wallet().getBalances()` twice during `setModel`. 2) Use the cached balance if the user did not select any UTXO manually inside the wallet model `getAvailableBalance` call. ----------------------- As an extra note, this work born in [#25005](bitcoin/bitcoin#25005) but grew out of scope of it. ACKs for top commit: jarolrod: ACK 4584d30 hebasto: re-ACK 4584d30, only suggested changes and commit message formatting since my [recent](#598 (review)) review. Tree-SHA512: 6633ce7f9a82a3e46e75aa7295df46c80a4cd4a9f3305427af203c9bc8670573fa8a1927f14a279260c488cc975a08d238faba2e9751588086fea1dcf8ea2b28
2 parents 867f5fd + 4584d30 commit 6d4889a

File tree

7 files changed

+54
-39
lines changed

7 files changed

+54
-39
lines changed

src/qt/overviewpage.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,6 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
147147
{
148148
ui->setupUi(this);
149149

150-
m_balances.balance = -1;
151-
152150
// use a SingleColorIcon for the "out of sync warning" icon
153151
QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning"));
154152
ui->labelTransactionsStatus->setIcon(icon);
@@ -177,8 +175,9 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
177175
void OverviewPage::setPrivacy(bool privacy)
178176
{
179177
m_privacy = privacy;
180-
if (m_balances.balance != -1) {
181-
setBalance(m_balances);
178+
const auto& balances = walletModel->getCachedBalance();
179+
if (balances.balance != -1) {
180+
setBalance(balances);
182181
}
183182

184183
ui->listTransactions->setVisible(!m_privacy);
@@ -197,7 +196,6 @@ OverviewPage::~OverviewPage()
197196
void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
198197
{
199198
BitcoinUnit unit = walletModel->getOptionsModel()->getDisplayUnit();
200-
m_balances = balances;
201199
if (walletModel->wallet().isLegacy()) {
202200
if (walletModel->wallet().privateKeysDisabled()) {
203201
ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
@@ -276,14 +274,13 @@ void OverviewPage::setWalletModel(WalletModel *model)
276274
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
277275

278276
// Keep up to date with wallet
279-
interfaces::Wallet& wallet = model->wallet();
280-
interfaces::WalletBalances balances = wallet.getBalances();
281-
setBalance(balances);
277+
setBalance(model->getCachedBalance());
282278
connect(model, &WalletModel::balanceChanged, this, &OverviewPage::setBalance);
283279

284280
connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit);
285281

286-
updateWatchOnlyLabels(wallet.haveWatchOnly() && !model->wallet().privateKeysDisabled());
282+
interfaces::Wallet& wallet = model->wallet();
283+
updateWatchOnlyLabels(wallet.haveWatchOnly() && !wallet.privateKeysDisabled());
287284
connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) {
288285
updateWatchOnlyLabels(showWatchOnly && !walletModel->wallet().privateKeysDisabled());
289286
});
@@ -306,10 +303,10 @@ void OverviewPage::changeEvent(QEvent* e)
306303

307304
void OverviewPage::updateDisplayUnit()
308305
{
309-
if(walletModel && walletModel->getOptionsModel())
310-
{
311-
if (m_balances.balance != -1) {
312-
setBalance(m_balances);
306+
if (walletModel && walletModel->getOptionsModel()) {
307+
const auto& balances = walletModel->getCachedBalance();
308+
if (balances.balance != -1) {
309+
setBalance(balances);
313310
}
314311

315312
// Update txdelegate->unit with the current unit

src/qt/overviewpage.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ public Q_SLOTS:
5252
Ui::OverviewPage *ui;
5353
ClientModel *clientModel;
5454
WalletModel *walletModel;
55-
interfaces::WalletBalances m_balances;
5655
bool m_privacy{false};
5756

5857
const PlatformStyle* m_platform_style;

src/qt/sendcoinsdialog.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,9 @@ void SendCoinsDialog::setModel(WalletModel *_model)
164164
}
165165
}
166166

167-
interfaces::WalletBalances balances = _model->wallet().getBalances();
168-
setBalance(balances);
169167
connect(_model, &WalletModel::balanceChanged, this, &SendCoinsDialog::setBalance);
170-
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::updateDisplayUnit);
171-
updateDisplayUnit();
168+
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::refreshBalance);
169+
refreshBalance();
172170

173171
// Coin Control
174172
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
@@ -711,9 +709,9 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
711709
}
712710
}
713711

714-
void SendCoinsDialog::updateDisplayUnit()
712+
void SendCoinsDialog::refreshBalance()
715713
{
716-
setBalance(model->wallet().getBalances());
714+
setBalance(model->getCachedBalance());
717715
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
718716
updateSmartFeeLabel();
719717
}
@@ -786,7 +784,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
786784
m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
787785

788786
// Calculate available amount to send.
789-
CAmount amount = model->wallet().getAvailableBalance(*m_coin_control);
787+
CAmount amount = model->getAvailableBalance(m_coin_control.get());
790788
for (int i = 0; i < ui->entries->count(); ++i) {
791789
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
792790
if (e && !e->isHidden() && e != entry) {

src/qt/sendcoinsdialog.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ private Q_SLOTS:
9797
void on_buttonMinimizeFee_clicked();
9898
void removeEntry(SendCoinsEntry* entry);
9999
void useAvailableBalance(SendCoinsEntry* entry);
100-
void updateDisplayUnit();
100+
void refreshBalance();
101101
void coinControlFeatureChanged(bool);
102102
void coinControlButtonClicked();
103103
void coinControlChangeChecked(int);

src/qt/test/wallettests.cpp

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
129129
QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
130130
}
131131

132+
void CompareBalance(WalletModel& walletModel, CAmount expected_balance, QLabel* balance_label_to_check)
133+
{
134+
BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit();
135+
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, expected_balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
136+
QCOMPARE(balance_label_to_check->text().trimmed(), balanceComparison);
137+
}
138+
132139
//! Simple qt wallet tests.
133140
//
134141
// Test widgets can be debugged interactively calling show() on them and
@@ -195,15 +202,10 @@ void TestGUI(interfaces::Node& node)
195202
sendCoinsDialog.setModel(&walletModel);
196203
transactionView.setModel(&walletModel);
197204

198-
{
199-
// Check balance in send dialog
200-
QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance");
201-
QString balanceText = balanceLabel->text();
202-
BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit();
203-
CAmount balance = walletModel.wallet().getBalance();
204-
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
205-
QCOMPARE(balanceText, balanceComparison);
206-
}
205+
// Update walletModel cached balance which will trigger an update for the 'labelBalance' QLabel.
206+
walletModel.pollBalanceChanged();
207+
// Check balance in send dialog
208+
CompareBalance(walletModel, walletModel.wallet().getBalance(), sendCoinsDialog.findChild<QLabel*>("labelBalance"));
207209

208210
// Send two transactions, and verify they are added to transaction list.
209211
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
@@ -223,12 +225,8 @@ void TestGUI(interfaces::Node& node)
223225
// Check current balance on OverviewPage
224226
OverviewPage overviewPage(platformStyle.get());
225227
overviewPage.setWalletModel(&walletModel);
226-
QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
227-
QString balanceText = balanceLabel->text().trimmed();
228-
BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit();
229-
CAmount balance = walletModel.wallet().getBalance();
230-
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
231-
QCOMPARE(balanceText, balanceComparison);
228+
walletModel.pollBalanceChanged(); // Manual balance polling update
229+
CompareBalance(walletModel, walletModel.wallet().getBalance(), overviewPage.findChild<QLabel*>("labelBalance"));
232230

233231
// Check Request Payment button
234232
ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get());

src/qt/walletmodel.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ WalletModel::~WalletModel()
6767

6868
void WalletModel::startPollBalance()
6969
{
70+
// Update the cached balance right away, so every view can make use of it,
71+
// so them don't need to waste resources recalculating it.
72+
pollBalanceChanged();
73+
7074
// This timer will be fired repeatedly to update the balance
7175
// Since the QTimer::timeout is a private signal, it cannot be used
7276
// in the GUIUtil::ExceptionSafeConnect directly.
@@ -120,12 +124,17 @@ void WalletModel::pollBalanceChanged()
120124

121125
void WalletModel::checkBalanceChanged(const interfaces::WalletBalances& new_balances)
122126
{
123-
if(new_balances.balanceChanged(m_cached_balances)) {
127+
if (new_balances.balanceChanged(m_cached_balances)) {
124128
m_cached_balances = new_balances;
125129
Q_EMIT balanceChanged(new_balances);
126130
}
127131
}
128132

133+
interfaces::WalletBalances WalletModel::getCachedBalance() const
134+
{
135+
return m_cached_balances;
136+
}
137+
129138
void WalletModel::updateTransaction()
130139
{
131140
// Balance and number of transactions might have changed
@@ -194,7 +203,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
194203
return DuplicateAddress;
195204
}
196205

197-
CAmount nBalance = m_wallet->getAvailableBalance(coinControl);
206+
// If no coin was manually selected, use the cached balance
207+
// Future: can merge this call with 'createTransaction'.
208+
CAmount nBalance = getAvailableBalance(&coinControl);
198209

199210
if(total > nBalance)
200211
{
@@ -602,3 +613,8 @@ uint256 WalletModel::getLastBlockProcessed() const
602613
{
603614
return m_client_model ? m_client_model->getBestBlockHash() : uint256{};
604615
}
616+
617+
CAmount WalletModel::getAvailableBalance(const CCoinControl* control)
618+
{
619+
return control && control->HasSelected() ? wallet().getAvailableBalance(*control) : getCachedBalance().balance;
620+
}

src/qt/walletmodel.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ class WalletModel : public QObject
155155

156156
uint256 getLastBlockProcessed() const;
157157

158+
// Retrieve the cached wallet balance
159+
interfaces::WalletBalances getCachedBalance() const;
160+
161+
// If coin control has selected outputs, searches the total amount inside the wallet.
162+
// Otherwise, uses the wallet's cached available balance.
163+
CAmount getAvailableBalance(const wallet::CCoinControl* control);
164+
158165
private:
159166
std::unique_ptr<interfaces::Wallet> m_wallet;
160167
std::unique_ptr<interfaces::Handler> m_handler_unload;

0 commit comments

Comments
 (0)