-
Notifications
You must be signed in to change notification settings - Fork 37.7k
"PSBT Operations" dialog #18027
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
"PSBT Operations" dialog #18027
Conversation
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
Code at b58e6f7 looks mostly alright, but Travis is very sad.
The first time I compiled this on macOS I get a cryptic error when I select the menu item:
2020-01-30 11:09:43.908 bitcoin-qt[619:453974] +[NSXPCSharedListener endpointForReply:withListenerName:]: an error occurred while attempting to obtain endpoint for listener 'com.apple.view-bridge': Connection interrupted
It worked fine the second time, so perhaps just some unrelated issue due to earlier code I compiled.
Ideally disable the sign button if there's nothing we can sign.
display status information is maybe unusual (a status bar, rather than messageboxes.)
I like it.
Currently this only changes the Load PSBT flow. Do you also want to apply this dialog to the send / bump fee flow? I'm fine with doing that later too.
You could also add a menu option to open a PSBT that's currently on the clipboard (e.g. handy when you need to save it as binary).
src/qt/bitcoingui.cpp
Outdated
@@ -317,6 +317,8 @@ void BitcoinGUI::createActions() | |||
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them")); | |||
verifyMessageAction = new QAction(tr("&Verify message..."), this); | |||
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses")); | |||
m_load_psbt_action = new QAction(tr("Load PSBT..."), 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.
You should pick a letter to turn into the keyboard shortcut (works on some operating systems, not macOS). E.g. "&Load PSBT..." if
L` isn't taken.
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.
Just went with L -- it's hard for me to check what's going on here since I'm on a Mac, but I don't see it as being taken. (I didn't add a shortcut for loading from clipboard, since L is taken, C is taken, I could use P for PSBT but it seems kind of unnatural.
src/qt/psbtoperationsdialog.h
Outdated
@@ -0,0 +1,52 @@ | |||
// Copyright (c) 2011-2014 The Bitcoin Core developers |
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: fix year
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.
Done.
src/util/error.cpp
Outdated
@@ -14,7 +14,7 @@ std::string TransactionErrorString(const TransactionError err) | |||
case TransactionError::OK: | |||
return "No error"; | |||
case TransactionError::MISSING_INPUTS: | |||
return "Missing inputs"; | |||
return "Inputs missing or spent"; |
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.
Cleaning these error messages could probably be its own commit.
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.
Yeah, agreed, will fix.
src/qt/psbtoperationsdialog.cpp
Outdated
|
||
switch (analysis.next) { | ||
case PSBTRole::UPDATER: { | ||
showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARNING); |
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.
I wonder if these strings can be moved to a place where the RPC can also use them.
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 would make sense actually, yeah. I could move them to wherever AnalyzePSBT is. What would the impact of this be on translation issues? I know that tr() is only used for GUI strings, and _() is used for non-GUI strings -- is there precedent for shared strings?
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.
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.
I know that tr() is only used for GUI strings, and _() is used for non-GUI strings -- is there precedent for shared strings?
Actually, bilingual_str
type from util/translation.h
supports both original and translated strings. More details are available in #16362 and #16224.
You could use something like this:
#include <util/translation.h>
bilingual_str bs = _("Transaction is missing some information about inputs.");
showStatus(bs.translated, StatusLevel::WARNING);
LogPrintf(bs.original);
return tx_description.toStdString(); | ||
} | ||
|
||
void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) { |
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.
Should we create a OperationsDialog
class to make this stuff reusable? (can wait of course)
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.
I'm happy to go either way, are there other existing dialogs we'd immediately want to adopt it for? I do like it better than messageboxes, I tried to make it as drop-in as possible a replacement for them.
src/qt/psbtoperationsdialog.cpp
Outdated
|
||
#include <iostream> | ||
|
||
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction &psbt) { |
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.
Maybe move this to psbt.h
?
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.
Can do, was dithering about what to do with it.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
Can I determine that without actually trying to sign? I was assuming that (1) I cannot, and (2) I shouldn't try to actually sign for real just to see if it works. (Or do you mean just disable the button at the point where we are asked to sign, try, and fail?)
I'd rather do that separately, I am trying really hard to cure myself of the disease of biting off way more than I can reasonably do in one go.
This I'd be happy to add, since it's a very small change. Just another item below "Load PSBT...", perhaps "Load PSBT from clipboard..."? |
I indeed wouldn't try actually signing. But you e.g. tell if a wallet is watch-only, and the new ScriptPubKeyManager's can tell if they can sign for things. |
Aha, and the Travis failure is the same issue we were talking about yesterday in #bitcoin-core-dev, I got scooped by #17261 and need to rebase again and fix errors, will do. EDIT: Oh, I think actually the Travis failure is just because master was already failing Travis at the moment I made the PR. So I think it will pass as soon as I push again. |
Ok, I'm adding a check so that we do not enable the sign button (and we show a helpful note) if the current wallet is privateKeysDisabled(). I would like to use ScriptPubKeyMan to get a better verdict, but it doesn't seem like the functionality I need is quite there. It looks like what I want is something like CWallet::GetSigningProvider, which calls CanProvide on each ScriptPubKeyMan in the wallet; except that the implementation of LegacyScriptPubKeyMan::CanProvide looks like it can return true in cases where we can't actually sign, but "We can still provide some stuff if we have the script". So I can't tell if there's a call I can make that just answers the question "do we believe that this wallet can sign", without myself looping over all our ScriptPubKeyMans, and trying ProduceSignature with each with a DUMMY_SIGNATURE_CREATOR. Obviously if this is true, a better approach would be to enhance the CWallet/ScriptPubKeyMan interface to provide this, but I think that's best left for another PR. (Or if this is already provided and I'm not seeing how, please fill me in.) |
Actually, here's an approach that seems like it should work -- FillPSBT already does basically all this work for us, since it does all the signing using a HidingSigningProvider, and allows us to tell it not to sign. So if I correctly understand the behavior of HidingSigningProvider, I think this should already tell us how many inputs we could sign, it just doesn't expose the result. If that seems correct to you, I can expose that from FillPSBT, and determine that way where there's anything for us to do here. @Sjors do you know whether this sounds correct? EDIT: No, this does not work. LegacyScriptPubKeyMan::CanProvide is playing some clever games with ProduceSignature in order to answer the "could we sign" question, and I don't want to duplicate those clever games into the PSBT logic. SON OF EDIT: Ok, a slightly different method does work -- in FillPSBT we can effectively check whether keys are available by whether pwallet->GetSigningProvider returns anything. This ultimately calls LegacyScriptPubKeyMan::CanProvide, which has the apparent issue I described above -- it says it returns true if we don't have keys, but we do have the script -- but I don't understand exactly what that means to try to produce an example that would confuse it. I do at least have something that works on the obvious cases now. |
b58e6f7
to
078e3ca
Compare
Updated:
Did not change:
Other notes:
Please take another look? |
Concept ACK. |
078e3ca
to
3e7ca1e
Compare
Speculatively attempting to fix the Windows-only fails without a Windows machine to test on. It looks like MinGW defines "ERROR" as a macro, so I can't give an enum constant that name. And it looks like the Visual Studio project file for building Bitcoin-QT is not autogenerated from the Makefiles, but has to be manually updated, so I've attempted to update it. |
In FillPSBT, optionally report the number of inputs we successfully signed, as an out parameter. If "sign" is false, instead report the number of inputs for which GetSigningProvider does not return nullptr. (This is a potentially overbroad estimate of inputs we could sign.) Github-Pull: bitcoin#18027 Rebased-From: 665c8e6
Add a "PSBT Operations" dialog, reached from the "Load PSBT..." menu item, giving options to sign or broadcast the loaded PSBT as appropriate, as well as copying the result to the clipboard or saving it to a file. Github-Pull: bitcoin#18027 Rebased-From: 3a9939c
Github-Pull: bitcoin#18027 Rebased-From: 3e7ca1e
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.
tACK 3e7ca1e (conditional on original PR being merged)
Would be nice to add change detection in the PSBT summary, but it can wait for a followup.
showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARN); | ||
break; | ||
} | ||
case PSBTRole::SIGNER: { |
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.
When all inputs have "next": "finalizer"
the transaction itself may still be "next": "signer"
. This seems to be a bug (@achow101 or feature?) in analyzepsbt
. If it's a bug, we can ignore it for this PR, if it's a feature, we should handle it, though imo it can wait for a followup.
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.
I am going to assume this is a bug, so I'm happy to push to a followup. I have been tempted to try to rewrite the flow of AnalyzePSBT, with an eye to simplifying it and making its correctness easier to check.
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.
Sounds like a 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.
Concept ACK! |
Mmm, it's misinterpreting a 2-of-3 multisig with two signed outputs. Probably related to my earlier comment. This is more annoying though, because it means I can't broadcast without the third signature. |
@Sjors are you able to supply me with an exact process to reproduce this? (Or an exact description -- is this a 2-of-3 using keys A, B, C, where both A and B have already signed, and you have C in your wallet, so it should be signable?) I don't think I'm relying on analyzepsbt for this determination -- I will have to look back at the code, but I should be saying that we can sign as long as GetSigningProvider does not return nullptr. So this might just be a simple bug, or I might have misunderstood something fundamental about when we can and can't sign, but either way I'll take a look, thanks! |
It's a watch-only wallet with a 2-of-3 multisig (three hardware wallets). Two of them signed. You probably want to call finalize on a copy of the PSBT just to see if it's complete. |
What is it misinterpreting? That is, what do you expect it to say? (If it's a watch-only wallet, then "this wallet does not have the right keys" should really say "this wallet cannot make signatures", because it's supposed to check privateKeysDisabled() first.) |
The Broadcast Transaction button should not be disabled. I can finalize it with the RPC and |
2a617cd
to
ca2144f
Compare
Add a "PSBT Operations" dialog, reached from the "Load PSBT..." menu item, giving options to sign or broadcast the loaded PSBT as appropriate, as well as copying the result to the clipboard or saving it to a file.
ca2144f
to
931dd47
Compare
It seems like @jb55's issue is consistent with FillPSBT not finalizing the transaction like I expected that it would, variably depending on which wallet is loaded. (@jb55, is one or both of the wallets a descriptor wallet by any chance?) There are really two issues: (1) we're trying to finalize the transaction when we load it, so that we know whether it's ready or not, but failing. (2) our two information sources are not in sync (one of them we are calculating directly, the other we are getting from AnalyzePSBT, which appears to do its own finalization when checking if all signatures are present.) I think I called FillPSBT because I wanted to know whether it thinks the PSBT is So I think the fix to (1) is for me to add a call to FinalizePSBT in openWithPSBT before I call fillPSBT, which I have just done. And as for (2) I think it's okay to ignore it for now (it's not ideal that we can theoretically display conflicting information like this, but it's purely a display issue and shouldn't happen unless something else is buggy.) The fix is harmless even if I'm wrong, so I think it's okay to call it good at this point. But I would still really really like a repro test case to play with, @jb55. (If I have correctly deduced fillPSBT's behavior, I find it surprising.) Can we officially declare this PR done and ready (and pray that this is the last time it will need a rebase?) :-) |
concept ACK fix, going to test again |
tested ACK 931dd47 Tested across multiple Core wallets and HWI |
re-tACK 931dd47 |
Concept ACK |
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 931dd47
@@ -617,6 +620,14 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb | |||
SignatureData sigdata; | |||
input.FillSignatureData(sigdata); | |||
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type); | |||
|
|||
bool signed_one = PSBTInputSigned(input); | |||
if (n_signed && (signed_one || !sign)) { |
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.
The intent is to indicate whether signing happened, or whether we could sign. But this is really checking whether we could finalize, which may include signing. For instance, this wouldn't work if the input were multisig.
But that might be ok for this PR.
To reduce code duplication, maybe this should go inside SignPSBTInput
?
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.
I'm very interested in a conversation about refactoring some of this stuff. Ok to save for another PR?
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.
(I am interested both in (1) reducing code duplication and (2) working out more precisely whether we can sign something, which I found to be very difficult to actually compute, and I definitely recognize this as a conservative approximation.)
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.
Ok to save for another PR?
Yep
ACK 931dd47 |
Concept ACK, glanced at the code, looks RTM |
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.
Light code review and partially tested ACK 931dd47
Good to see this move forward and looking forward to continued progress as described in #18027 (comment). A couple of nits for the next PR follow, no need to respond to them.
bool signed_one = PSBTInputSigned(input); | ||
if (n_signed && (signed_one || !sign)) { | ||
// If sign is false, we assume that we _could_ sign if we get here. This | ||
// will never have false negatives; it is hard to tell under what i |
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: s/what i/what/ here and at line 2136 below
@@ -11,6 +11,7 @@ | |||
#include <qt/askpassphrasedialog.h> | |||
#include <qt/clientmodel.h> | |||
#include <qt/guiutil.h> | |||
#include <qt/psbtoperationsdialog.h> |
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: sort
….cpp 4f9d9ef qt: Remove needless headers (Hennadii Stepanov) Pull request description: No symbols from the removed headers are used in the `qt/walletview.cpp`. This is a small followup of #18027. Top commit has no ACKs. Tree-SHA512: 986ed5c8f3bac4c0053736ce84d738f8593d3dbf713109af3cb9b7051cd838f23152a39bb3c1e9694a993c4e7accf14e94e5beff5e7881155638cd44fbf7f46f
… checking for completeness Github-Pull: bitcoin#18027 Rebased-From: a6cb0b0
….cpp 4f9d9ef qt: Remove needless headers (Hennadii Stepanov) Pull request description: No symbols from the removed headers are used in the `qt/walletview.cpp`. This is a small followup of bitcoin#18027. Top commit has no ACKs. Tree-SHA512: 986ed5c8f3bac4c0053736ce84d738f8593d3dbf713109af3cb9b7051cd838f23152a39bb3c1e9694a993c4e7accf14e94e5beff5e7881155638cd44fbf7f46f
….cpp 4f9d9ef qt: Remove needless headers (Hennadii Stepanov) Pull request description: No symbols from the removed headers are used in the `qt/walletview.cpp`. This is a small followup of bitcoin#18027. Top commit has no ACKs. Tree-SHA512: 986ed5c8f3bac4c0053736ce84d738f8593d3dbf713109af3cb9b7051cd838f23152a39bb3c1e9694a993c4e7accf14e94e5beff5e7881155638cd44fbf7f46f
….cpp 4f9d9ef qt: Remove needless headers (Hennadii Stepanov) Pull request description: No symbols from the removed headers are used in the `qt/walletview.cpp`. This is a small followup of bitcoin#18027. Top commit has no ACKs. Tree-SHA512: 986ed5c8f3bac4c0053736ce84d738f8593d3dbf713109af3cb9b7051cd838f23152a39bb3c1e9694a993c4e7accf14e94e5beff5e7881155638cd44fbf7f46f
….cpp 4f9d9ef qt: Remove needless headers (Hennadii Stepanov) Pull request description: No symbols from the removed headers are used in the `qt/walletview.cpp`. This is a small followup of bitcoin#18027. Top commit has no ACKs. Tree-SHA512: 986ed5c8f3bac4c0053736ce84d738f8593d3dbf713109af3cb9b7051cd838f23152a39bb3c1e9694a993c4e7accf14e94e5beff5e7881155638cd44fbf7f46f
Add a "PSBT Operations" dialog, reached from the "Load PSBT..." menu item, giving options to sign or broadcast the loaded PSBT as appropriate, as well as copying the result to the clipboard or saving it to a file.
This is based on Sjors' #17509, and depends on that PR going in first. (It effectively replaces the small "load PSBT" dialog from that PR with a more feature-rich one.)
Some notes: