Skip to content

Conversation

furszy
Copy link
Member

@furszy furszy commented Dec 14, 2022

Fixes bitcoin-core/gui#688 and #26687.

First Issue Description (bitcoin-core/gui#688):

The previous behavior for getAvailableBalance, when the coin control had selected coins, was to return the sum of them. Instead, we are currently returning the wallet's available total balance minus the selected coins total amount.

Reason:
Missed to update the GetAvailableBalance function to include the coin control selected coins on #25685.

Context:
Since #25685 we skip the selected coins inside AvailableCoins, the reason is that there is no need to waste resources walking through the entire wallet's txes map just to get coins that could have gotten by just doing a simple mapWallet.find).

Places Where This Generates Issues (only when the user manually select coins via coin control):

  1. The GUI balance check prior the transaction creation process.
  2. The GUI "useAvailableBalance" functionality.

Note 1:
As the GUI uses a balance cache since bitcoin-core/gui#598, this issue does not affect the regular spending process. Only arises when the user manually select coins.

Note 2:
Added test coverage for the useAvailableBalance functionality.


Second Issue Description (#26687):

As we are using a cached balance on WalletModel::getAvailableBalance,
the function needs to include the watch-only available balance for wallets
with private keys disabled.

@DrahtBot
Copy link
Contributor

DrahtBot commented Dec 14, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK Sjors, achow101, theStack

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #bitcoin-core/gui/417 (Ditch wallet model juggling by promag)
  • #27286 (wallet: Keep track of the wallet's own transaction outputs in memory by achow101)
  • #27217 (wallet: Replace use of purpose strings with an enum by achow101)
  • #26715 (Introduce MockableDatabase for wallet unit tests by achow101)
  • #25979 ([WIP] wallet: standardize change output detection process by furszy)
  • #22838 (descriptors: Be able to specify change and receiving in a single descriptor string by achow101)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@furszy furszy changed the title wallet, gui: getAvailableBalance skips selected coins wallet, gui: bugfix, getAvailableBalance skips selected coins Dec 14, 2022
@achow101
Copy link
Member

While you're in this function, can you also fix #26687. It's a similar issue - we're now filtering for only spendable coins which excludes watch-only coins.

@furszy
Copy link
Member Author

furszy commented Dec 14, 2022

While you're in this function, can you also fix #26687. It's a similar issue - we're now filtering for only spendable coins which excludes watch-only coins.

sure @achow101, will check it. Feel free to cc me for other similar stuff next time.

@furszy furszy force-pushed the 2022_bugfix_wallet_getavailablebalance branch from 197fbb7 to 30785b2 Compare December 15, 2022 01:46
@furszy
Copy link
Member Author

furszy commented Dec 15, 2022

Added fix and test coverage for #26687.

As we are using a cached balance on WalletModel::getAvailableBalance,
the function needs to include into the response the watch-only available
balance on wallets with private keys disabled.

Note:
Let me know if want me to decouple this two fixes into a new PR.

@furszy furszy force-pushed the 2022_bugfix_wallet_getavailablebalance branch from 30785b2 to e680c2d Compare December 16, 2022 23:10
@furszy
Copy link
Member Author

furszy commented Dec 17, 2022

Added test coverage for the GUI PSBT creation on legacy watch-only wallets.

Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK

Saw this problem yesterday while I was experimenting with the GUI and noticed that trying to spend a single available UTXO via coin control failed with a "The amount exceeds your balance." error -- IIUC, this issue appears whenever the balance of the not-selected coins is smaller than the balance of the selected coins.

@maflcko
Copy link
Member

maflcko commented Jan 18, 2023

Needs rebase

@furszy furszy force-pushed the 2022_bugfix_wallet_getavailablebalance branch 2 times, most recently from 941cdc1 to 705d44d Compare January 18, 2023 16:40
@furszy
Copy link
Member Author

furszy commented Jan 18, 2023

rebased, silent conflict solved.

@furszy furszy force-pushed the 2022_bugfix_wallet_getavailablebalance branch from 705d44d to 48e5173 Compare January 18, 2023 22:25
@Sjors
Copy link
Member

Sjors commented Mar 31, 2023

Tested that this fixes the coin selection + use available balance GUI issue for me. Haven't reviewed the code yet.

@furszy
Copy link
Member Author

furszy commented Apr 2, 2023

rebased, conflict solved.

Copy link
Member

@Sjors Sjors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and reviewed 86b43c7.

Especially happy with all the new tests. Some questions / issues:

@@ -403,7 +404,24 @@ class WalletImpl : public Wallet
CAmount getBalance() override { return GetBalance(*m_wallet).m_mine_trusted; }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
{
return GetAvailableBalance(*m_wallet, &coin_control);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8cd7730: paging @ryanofsky: do you think we should move all this complexity here to the interface?

Copy link
Contributor

@ryanofsky ryanofsky Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8cd7730: paging @ryanofsky: do you think we should move all this complexity here to the interface?

Right, probably it would be better to move the new code from the wallet:WalletImpl::getAvailableBalance() method in src/wallet/interfaces.cpp into the wallet::GetAvailableBalance() function in src/wallet/spend.cpp or into another function there like wallet::GetSelectedCoinsBalance. Reasoning is

  • src/wallet/interfaces.cpp is supposed to define a public interface to wallet code that GUI code can call without accessing wallet internals. It is just supposed to forward calls from the public interface to private functions, and not really designed to contain coin selection code.
  • It is easier to write unit tests for standalone spend.cpp functions than interface methods that require constructing a larger interface.
  • It is easier to share code between RPC and GUI interfaces right now if code is standalone

EDIT: To clarify, I think it would be better to move this code, but I don't think leaving the code here is a big problem. I wouldn't want it to delay fixing the bug.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Having a proxy class is nice for any future IPC mechanism. All the protobuf/protocan/etc messages are simpler to craft in this way.

Based on how close we are to v25 branch-off, I'm thinking on whether should do it now or later :/. Otherwise I would push it right away.

Probably, could tackle it on a quick follow-up with #26699 (comment) and few other cleanups as well.

furszy added 6 commits April 3, 2023 17:23
The previous behavior for getAvailableBalance when coin control
has selected coins was to return the sum of them. Instead, we
are currently returning the wallet's available total balance minus
the selected coins total amount.

This turns into a GUI-only issue for the "use available balance"
button when the user manually select coins in the send screen.

Reason:
We missed to update the GetAvailableBalance function to include
the coin control selected coins on bitcoin#25685.

Context:
Since bitcoin#25685 we skip the selected coins inside `AvailableCoins`,
the reason is that there is no need to traverse the wallet's
txes map just to get coins that can directly be fetched by
their id.
The following cases were covered:

Case 1: No coin control selected coins.
  - 'useAvailableBalance' should fill the amount edit box with the total available balance.

Case 2: With coin control selected coins.
  - 'useAvailableBalance' should fill the amount edit box with the sum of the selected coins values.
Only for wallets with private keys disabled.

The returned amount need to include the watch-only
available balance too.

Solves bitcoin#26687.
Prepare ground for legacy watch-only test.
@furszy furszy force-pushed the 2022_bugfix_wallet_getavailablebalance branch from 86b43c7 to 68eed5d Compare April 3, 2023 20:24
@furszy
Copy link
Member Author

furszy commented Apr 3, 2023

Feedback tackled. Thanks Sjors.

Only a comment change, diff.

@Sjors
Copy link
Member

Sjors commented Apr 4, 2023

tACK 68eed5d

Remaining feedback is only about source code comments.

@achow101
Copy link
Member

ACK 68eed5d

@achow101 achow101 requested a review from theStack April 10, 2023 13:37
Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 68eed5d

Reviewed the changeset (with only light review for the qt unit tests, as I'm not too familiar in that area), verified manually that the GUI coin selection issue is fixed, tested the tests by reverting the fix commits dc1cc1c and cd98b71 each and checking that the corresponding unit tests fail. LGTM.

@@ -403,7 +403,7 @@ void SendCoinsDialog::presentPSBT(PartiallySignedTransaction& psbtx)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
QMessageBox msgBox;
QMessageBox msgBox(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is there any specific reason for this change? Without it, everything seems to work still fine on my side (but obviously, it shouldn't hurt to construct the message box with a parent...)

Copy link
Member Author

@furszy furszy Apr 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was because I was accessing the parent to click on the message box from the unit test (the dialog exec() method blocks the thread until the user presses any of the buttons). And, later on, changed my mind and accessed it via the QApplication::topLevelWidgets call.

It doesn't hurt to have it. This would had been a leak if the message box would had been constructed via the new call.

@achow101 achow101 merged commit 27dcc07 into bitcoin:master Apr 11, 2023
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Apr 12, 2023
@furszy furszy deleted the 2022_bugfix_wallet_getavailablebalance branch May 27, 2023 01:46
Comment on lines +415 to +429
QTimer timer;
timer.setInterval(500);
QObject::connect(&timer, &QTimer::timeout, [&](){
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("QMessageBox")) {
QMessageBox* dialog = qobject_cast<QMessageBox*>(widget);
QAbstractButton* button = dialog->button(QMessageBox::Discard);
button->setEnabled(true);
button->click();
timer.stop();
break;
}
}
});
timer.start(500);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems all these lines of code might be replaced with ConfirmMessage(nullptr, 500ms);.

Copy link
Member Author

@furszy furszy Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems all these lines of code might be replaced with ConfirmMessage(nullptr, 500ms);.

That will trigger the filesystem window to dump the psbt info to a file, which would incur in another step to cancel the dialog. The idea here is to obtain the data through the clipboard only (see few lines below), not through a file.

That being said, agree that we could move this into the utility file in the future and cleanup some code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will trigger the filesystem window to dump the psbt info to a file...

Why? The ConfirmMessage triggers the default button, which is QMessageBox::Discard, no?

Copy link
Member Author

@furszy furszy Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will trigger the filesystem window to dump the psbt info to a file...

Why? The ConfirmMessage triggers the default button, which is QMessageBox::Discard, no?

Yeah ok, I did not see the manual setDefaultButton call. That will work fine while the dialog takes less than 500ms in popping up. If it takes longer, the test will stall because ConfirmMessage runs the timer only once (QTimer::singleShot).

@bitcoin bitcoin locked and limited conversation to collaborators Feb 18, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Selecting two coins will abort with "The amount exceeds your balance."
8 participants