UI external signer support (e.g. hardware wallet)#4
UI external signer support (e.g. hardware wallet)#4meshcollider merged 7 commits intobitcoin-core:masterfrom
Conversation
|
The original bitcoin/bitcoin#16549 (comment) has a Concept ACK from @fjahr. I'll split out the interface changes and PR them to bitcoin/bitcoin later. |
29e6a9d to
3ad6941
Compare
…ders 0ecff9d Improve "detected inconsistent lock order" error message (Hennadii Stepanov) bbe9cf4 test: Improve "potential deadlock detected" exception message (Hennadii Stepanov) 3559934 Fix mistakenly swapped "previous" and "current" lock orders (Hennadii Stepanov) Pull request description: In master (8ef15e8) the "previous" and "current" lock orders are mistakenly swapped. This PR: - fixes printed lock orders - improves the `sync_tests` unit test - makes the "detected inconsistent lock order" error message pointing to the lock location rather `tfm::format()` location. Debugger output example with this PR (with modified code, of course): ``` 2020-06-22T15:46:56Z [msghand] POTENTIAL DEADLOCK DETECTED 2020-06-22T15:46:56Z [msghand] Previous lock order was: 2020-06-22T15:46:56Z [msghand] (2) 'cs_main' in net_processing.cpp:2545 (in thread 'msghand') 2020-06-22T15:46:56Z [msghand] (1) 'g_cs_orphans' in net_processing.cpp:1400 (in thread 'msghand') 2020-06-22T15:46:56Z [msghand] Current lock order is: 2020-06-22T15:46:56Z [msghand] (1) 'g_cs_orphans' in net_processing.cpp:2816 (in thread 'msghand') 2020-06-22T15:46:56Z [msghand] (2) 'cs_main' in net_processing.cpp:2816 (in thread 'msghand') Assertion failed: detected inconsistent lock order for 'cs_main' in net_processing.cpp:2816 (in thread 'msghand'), details in debug log. Process 131393 stopped * thread bitcoin-core#15, name = 'b-msghand', stop reason = signal SIGABRT frame #0: 0x00007ffff775c18b libc.so.6`__GI_raise(sig=2) at raise.c:51:1 (lldb) bt * thread bitcoin-core#15, name = 'b-msghand', stop reason = signal SIGABRT * frame #0: 0x00007ffff775c18b libc.so.6`__GI_raise(sig=2) at raise.c:51:1 frame bitcoin-core#1: 0x00007ffff773b859 libc.so.6`__GI_abort at abort.c:79:7 frame bitcoin-core#2: 0x0000555555e5b196 bitcoind`(anonymous namespace)::potential_deadlock_detected(mismatch=0x00007fff99ff6f30, s1=size=2, s2=size=2, lock_location=0x00007fff99ff7010) at sync.cpp:134:9 frame bitcoin-core#3: 0x0000555555e5a1b1 bitcoind`(anonymous namespace)::push_lock(c=0x0000555556379220, locklocation=0x00007fff99ff7010) at sync.cpp:158:13 frame bitcoin-core#4: 0x0000555555e59e8a bitcoind`EnterCritical(pszName="cs_main", pszFile="net_processing.cpp", nLine=2816, cs=0x0000555556379220, fTry=false) at sync.cpp:177:5 frame bitcoin-core#5: 0x00005555555b0500 bitcoind`UniqueLock<AnnotatedMixin<std::recursive_mutex>, std::unique_lock<std::recursive_mutex> >::Enter(this=0x00007fff99ff8c20, pszName="cs_main", pszFile="net_processing.cpp", nLine=2816) at sync.h:134:9 frame bitcoin-core#6: 0x00005555555b017f bitcoind`UniqueLock<AnnotatedMixin<std::recursive_mutex>, std::unique_lock<std::recursive_mutex> >::UniqueLock(this=0x00007fff99ff8c20, mutexIn=0x0000555556379220, pszName="cs_main", pszFile="net_processing.cpp", nLine=2816, fTry=false) at sync.h:160:13 frame bitcoin-core#7: 0x00005555556aa57e bitcoind`ProcessMessage(pfrom=0x00007fff90001180, msg_type=error: summary string parsing error, vRecv=0x00007fff9c005ac0, nTimeReceived=1592840815980751, chainparams=0x00005555564b7110, chainman=0x0000555556380880, mempool=0x0000555556380ae0, connman=0x000055555657aa20, banman=0x00005555565167b0, interruptMsgProc=0x00005555565cae90) at net_processing.cpp:2816:9 ``` ACKs for top commit: laanwj: ACK 0ecff9d vasild: ACK 0ecff9d Tree-SHA512: ff285de8dd3198b5b33c4bfbdadf9b1448189c96143b9696bc4f41c07e784c00851ec169cf3ed45cc325f3617ba6783620803234f57fcce28bf6bc3d6a7234fb
|
Shouldn't the external signer program be per-wallet? |
|
@luke-jr in practice there's only 1 program out there: HWI. It already knows what to do for different wallets based on their fingerprint. So, unless more such tools show up, I prefer to wait until we can store arbitrary settings in the wallet: bitcoin/bitcoin#13044 and then keep |
fd7c7e1 to
e9b4675
Compare
e9b4675 to
7df0674
Compare
|
@Bosch-0 oops, I accidentally lost a commit in my rebase fury. Try again. |
|
Awesome, that worked! Having some issues with HWI though. After installing and setting the path to the hwi.py file I keep getting an exception error when creating a wallet. How would we overcome having to install HWI separately? Would it have to be something like shipping HWI alongside the GUI. I'm a not too familiar with this process but having the path set by default behind the scenes would be ideal from a UX perspective. |
|
What's the error you're seeing? Also, can you try from the command-line: |
|
Rebased an addressed (most of) @meshcollider's comments. |
|
Rebased and slightly clarified the last commit. |
There was a problem hiding this comment.
Tested 1c4b456 with HWW 2.0.2-rc.1 on Linux Mint 20.1 (Qt 5.12.8).
UPDATE: it was my fault -- hww connection issues. Ignore this.
The "External signer" checkbox is disabled when trying Menu -> File -> Create Wallet...
It could be enabled by checking the "Encrypt Wallet" checkbox, then unchecking it.
hebasto
left a comment
There was a problem hiding this comment.
ACK 1c4b456, tested on Linux Mint 20.1 (Qt 5.12.8) with HWW 2.0.2-rc.1.
Tested:
- build with
--without-bdbconfigure option - build with
--without-sqliteconfigure option - creating a wallet
- verifying an address on the hww screen
- signing a tx
The following comments could be addressed in follow ups.
-
If no external signer is connected, it is possible to activate the "External signer" checkbox by checking the "Encrypt Wallet" checkbox, then unchecking it. Of course, the further attempt to create a wallet with an external signer will ends with the crash. This illness pattern works even if the client is compiled without SQLite support:

-
The "Confirm send coins" could still be opened while verifying transaction details on a signing device.
-
All new translatable strings could be annotated with translator comments.
| // Disable the disable_privkeys_checkbox and external_signer_checkbox when isEncryptWalletChecked is | ||
| // set to true, enable it when isEncryptWalletChecked is false. | ||
| ui->disable_privkeys_checkbox->setEnabled(!checked); | ||
| ui->external_signer_checkbox->setEnabled(!checked); |
There was a problem hiding this comment.
This should take into account ENABLE_EXTERNAL_SIGNER.
May I suggest adding a private slot update or updateState and move all logic there?
There was a problem hiding this comment.
Adding a fix in bitcoin/bitcoin#21935. I agree this whole toggling code needs a refactor.
| connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning); | ||
| connect(ui->pruneSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); | ||
| connect(ui->databaseCache, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); | ||
| connect(ui->externalSignerPath, &QLineEdit::textChanged, [this]{ showRestartWarning(); }); |
There was a problem hiding this comment.
Should disable externalSignerPath if not defined ENABLE_EXTERNAL_SIGNER?
| ui->wallet_content->hide(); | ||
| } | ||
|
|
||
| ui->btnVerify->setVisible(this->model->wallet().hasExternalSigner()); |
| #ifdef ENABLE_EXTERNAL_SIGNER | ||
| std::vector<ExternalSigner> externalSigners() override | ||
| { | ||
| std::vector<ExternalSigner> signers = {}; |
| ui->blank_wallet_checkbox->setChecked(false); | ||
| ui->disable_privkeys_checkbox->setEnabled(false); | ||
| ui->disable_privkeys_checkbox->setChecked(true); | ||
| const std::string label = signers[0].m_name; |
There was a problem hiding this comment.
Could add comment "TODO show available signers in combobox, for now use 1st signer name"?
There was a problem hiding this comment.
@promag it is just the name of the wallet so I don't think it would be worth adding a combobox tbh. That might be confusing because it wouldn't actually change between signers, it would just change the name.
| virtual ~CreateWalletDialog(); | ||
|
|
||
| #ifdef ENABLE_EXTERNAL_SIGNER | ||
| void setSigners(std::vector<ExternalSigner>& signers); |
| return false; | ||
| } | ||
| #ifdef ENABLE_EXTERNAL_SIGNER | ||
| std::vector<ExternalSigner> externalSigners() override |
There was a problem hiding this comment.
Should we let this throw runtime_error? cc @ryanofsky
There was a problem hiding this comment.
Most of these ifdefs are gone in bitcoin/bitcoin#21935
|
Code Review ACK 1c4b456 Mostly reviewed the code, did a little bit of testing. I noticed that when HWI is doing something, there is no indication to the user that it is. It would be nice if there was some dialog box that said something was happening, otherwise there can be a minute or two where Core doesn't indicate anything is happening, and nothing appears on the device. |
There was a problem hiding this comment.
re-code-review ACK 1c4b456
Agree with achow101's comment about a 'working' message.
| ui->encrypt_wallet_checkbox->setChecked(false); | ||
| // The order matters, because connect() is called when toggling a checkbox: | ||
| ui->blank_wallet_checkbox->setEnabled(false); | ||
| ui->blank_wallet_checkbox->setChecked(false); |
There was a problem hiding this comment.
nit: to be consistent with gui: create wallet with external signer, this should be true (modulo comment about it being ambiguous anyway).
| ui->blank_wallet_checkbox->setChecked(false); | ||
| ui->disable_privkeys_checkbox->setEnabled(false); | ||
| ui->disable_privkeys_checkbox->setChecked(true); | ||
| const std::string label = signers[0].m_name; |
There was a problem hiding this comment.
@promag it is just the name of the wallet so I don't think it would be worth adding a combobox tbh. That might be confusing because it wouldn't actually change between signers, it would just change the name.
| } | ||
|
|
||
| if (model->wallet().privateKeysDisabled()) { | ||
| if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) { |
There was a problem hiding this comment.
What about noPrivateKeyAccess() or something - the condition is that there are keys either in the wallet itself or that the wallet can 'use' via the external signer. Not a big deal though.
|
post-merge ACK, wanted to document that I tested this out with a coldcard and the process was smooth. |
|
@Sjors I tried this with my trezor1, I unlocked it on the command line on a wallet with a bip39 password, this is what pops up when I click create wallet:
dunno if I'm doing something wrong |
|
Mmm, I'm not sure how the passphrase flow works. @achow101? I might be that we need to add support for that, e.g. if However in your case those are |
|
The passphrase should be cached by the device, so if you try again after running HWI manually, it should work. But we do need to implement a passphrase workflow for |
|
@achow101 I've never been able to get the trezor_1 passphrase cached with hwi, I've always had to do |
|
Oh right, Trezor 1 doesn't have that, only Trezor T. |
|
@achow101 fwiw it caches fine when using trezorctl, dunno what they are doing. |
|
Could you add release notes into https://github.com/bitcoin-core/bitcoin-devwiki/wiki/22.0-Release-Notes-draft#gui-changes? |
|
@hebasto done, also for the RPC. |





Big picture overview in this gist.
This PR adds GUI support for external signers, based on the since merged bitcoin/bitcoin#16546 (RPC).
The UX isn't amazing - especially the blocking calls - but it works.
First we adds a GUI setting for the signer script (e.g. path to HWI):
Then we add an external signer checkbox to the wallet creation dialog:
It's checked by default if HWI detects a device. It also grabs the name. It then creates a fresh wallet and imports the keys.
You can verify an address on the device (blocking...):

Sending, including coin selection, Just Works(tm) as long the device is present.
External signer support is enabled by default when the GUI is configured and Boost::Process is present.External signer support remains disabled by default, see bitcoin/bitcoin#21935.