-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet, gui: bugfix, getAvailableBalance skips selected coins #26699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wallet, gui: bugfix, getAvailableBalance skips selected coins #26699
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
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. |
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. |
197fbb7
to
30785b2
Compare
Added fix and test coverage for #26687. As we are using a cached balance on Note: |
30785b2
to
e680c2d
Compare
Added test coverage for the GUI PSBT creation on legacy watch-only wallets. |
cd28dc4
to
c1c1fcb
Compare
There was a problem hiding this 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.
Needs rebase |
941cdc1
to
705d44d
Compare
rebased, silent conflict solved. |
705d44d
to
48e5173
Compare
Tested that this fixes the coin selection + use available balance GUI issue for me. Haven't reviewed the code yet. |
37d8a00
to
86b43c7
Compare
rebased, conflict solved. |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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.
So it can be reused across tests.
86b43c7
to
68eed5d
Compare
Feedback tackled. Thanks Sjors. Only a comment change, diff. |
tACK 68eed5d Remaining feedback is only about source code comments. |
ACK 68eed5d |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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...)
There was a problem hiding this comment.
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.
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); |
There was a problem hiding this comment.
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);
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 isQMessageBox::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
).
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 simplemapWallet.find
).Places Where This Generates Issues (only when the user manually select coins via coin control):
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.