bitcoin-core / hwi Goto Github PK
View Code? Open in Web Editor NEWBitcoin Hardware Wallet Interface
License: MIT License
Bitcoin Hardware Wallet Interface
License: MIT License
Should be, eg. "Arbitrary scriptPubKey Inputs"
One of our users reported that the way Trezor displays tx confirmation change address, too. What can be done about it? Wait for Trezor to fix it in the driver or you're planning to fix it here?
Sending coins- the Trezor asks me to confirm the address as usual however the first address presented on it is NOT the destination address. I assumed, correctly, that this must be the address for the change, and as it was for a tiny amount I took the chance. The second address presented on the Trezor was the destination address as expected. There could do with being an option to see the “change address” prior to it appearing on the Trezor, it’s really unnerving to be authorising a transaction to an address I have not seen anywhere! And it makes the whole confirmation on the Trezor pointless as I’m not actually checking it against any known (to me) address.
I needs it.
At least on macOS you need to brew install libusb
, in addition to the pip3 install stuff, or you get the following error:
./hwi.py enumerate
WARNING:trezorlib.transport.webusb:WebUSB transport is disabled: dlopen(libusb-1.0.dylib, 6): image not found
[]
The device is detected by enumerate
if you set LEDGER_DEVICE_ID = 0x0004
. I'm able to get keys from it and display an address. Will try some other things later and make a PR.
At some point I would like to start doing proper versioning of HWI and release it as both a library and executable script on PyPi. The following need to be done before I feel comfortable doing this:
process_commands
from commands.py
and have the functions in commands.py
take actual arguments--debug
). The only thing that should be output is the result. (This may not be possible for Trezor and Keepkey, see #83)NotImplementedError
s--version
optionWould support simple interoperation with other wallets who only take them as files.
I just wanted to run the Ledger tests on the device, but ./setup_environment.sh
installs the whole kitchen sink. Would be nice if you can do ./setup_environment.sh --ledger
. This is especially useful on macOS where some of the script doesn't work yet.
It would be very nice to not be forced to use the centralized wallet portals to update, so putting the various python libraries to use to do the updates instead under one roof is desired.
We would likely have to put caveats on these commands since they'll likely "phone home" in some respect.
Ledger update would also have to include steps to update the bitcoin app itself(how?)
Now that all of Bitcoin Core's prerequisite PRs are merged, we should stop using the hww branch. We can either just build from the master branch, or download a binary from bitcoincore.org once 0.18 is released.
Also the docs should be updated.
While the enumerate
command works, neither inserting the fingerprint, nor the path, you can obtain anything functioning.
The message reports the path as an " invalid argument command" citing the path as it was a command.
./hwi.py enumerate
[{"type": "ledger", "path": "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/EHC1@fd000000/PRT1@fd100000/IOUSBHostDevice@fd100000/AppleUSB20InternalHub@fd100000/AppleUSB20HubPort@fd140000/Nano S@fd140000/Nano S@0/IOUSBHostHIDDevice@fd140000,0", "fingerprint": "xxxxxxxx"}]
./hwi.py --path "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/EHC1@fd000000/PRT1@fd100000/IOUSBHostDevice@fd100000/AppleUSB20InternalHub@fd100000/AppleUSB20HubPort@fd140000/Nano S@fd140000/Nano S@0/IOUSBHostHIDDevice@fd140000,0" getmasterxpub
usage: hwi.py [-h] [--device-path DEVICE_PATH] [--device-type DEVICE_TYPE]
[--password PASSWORD] [--stdinpass] [--testnet] [--debug]
[--fingerprint FINGERPRINT] [--version] [--stdin]
[--interactive]
{enumerate,getmasterxpub,signtx,getxpub,signmessage,getkeypool,displayaddress,setup,wipe,restore,backup,promptpin,sendpin}
...
hwi.py: error: argument command: invalid choice: 'IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/EHC1@fd000000/PRT1@fd100000/IOUSBHostDevice@fd100000/AppleUSB20InternalHub@fd100000/AppleUSB20HubPort@fd140000/Nano S@fd140000/Nano S@0/IOUSBHostHIDDevice@fd140000,0' (choose from 'enumerate', 'getmasterxpub', 'signtx', 'getxpub', 'signmessage', 'getkeypool', 'displayaddress', 'setup', 'wipe', 'restore', 'backup', 'promptpin', 'sendpin')
Also "--device-path" (suggested in the usage:) seems to be an unsupported option.
{'error': 'DataError: signing.c:849:Pubkey not found in multisig script', 'code': -7}
even when you don't want to sign the input.
The Trezor model T uses webusb to communicate (see #38 (comment)). I believe Ledger Nano S supports U2F.
I'm not entirely clear on the differences between webusb and U2F.
I'm guessing trying and studying https://github.com/trezor/python-trezor would be a good place to start.
We need to make sure that we give a proper error when the user is not in the Bitcoin app on the Ledger Nano S. I was unable to find a way to detect this. It seems like what happens is that there's just an error that gets thrown with an unhelpful error message, so maybe we just need to make the error messages better?
@instagibbs Can you take a look?
I followed the tutorial on my coldcard and it did not succeed to do importmulti
.
https://github.com/bitcoin-core/HWI/blob/master/docs/bitcoin-core-usage.md
hwi --testnet -f 7a88e7a2 getkeypool --wpkh --keypool 0 1000
[{"desc": "wpkh([7a88e7a2/84h/1h/0h]tpubDC7pT42Gr8ox24agqaaKnYHkcapDcaeRiQLwxiv91TxBWxuCdp8LHsuTiQqHqrCXNb8jjCMztw6aZsYokYbAYjdCbunBk2E2DDTqtDUw1s4/0/*)#caxltj90", "range": [0, 1000], "timestamp": "now", "internal": false, "keypool": true, "watchonly": true}]
bitcoin-cli.exe --testnet -rpcwallet=coldcard importmulti '[{"desc": "wpkh([7a88e7a2/84h/1h/0h]tpubDC7pT42Gr8ox24agqaaKnYHkcapDcaeRiQLwxiv91TxBWxuCdp8LHsuTiQqHqrCXNb8jjCMztw6aZsYokYbAYjdCbunBk2E
2DDTqtDUw1s4/0/*)#caxltj90", "range": [0, 1000], "timestamp": "now", "internal": false, "keypool": true, "watchonly": true}]'
error: Error parsing JSON:[{desc: wpkh([7a88e7a2/84h/1h/0h]tpubDC7pT42Gr8ox24agqaaKnYHkcapDcaeRiQLwxiv91TxBWxuCdp8LHsuTiQqHqrCXNb8jjCMztw6aZsYokYbAYjdCbunBk2E2DDTqtDUw1s4/0/*)#caxltj90, range: [0, 1000], timestamp: now, internal: false, keypool: true, watchonly: true}]
TestNet, Windows 10, tried to use both cmd
and PowerShell
.
When I saw Core was entering this area I hoped older devices like the Ledger Nano (no S) might be included?
it grabs device by fingerprint, which isn't set up by then
This should make it easier to check if there are any unspent funds without having to sit through a long rescan.
./hwi.py --device-type=ledger scantxoutset --bech32 [--paths [desc1 desc2 desc3 etc]] [range=1000]
It would use BIP44/49/84 by default and produce descriptors for both receive and change chains.
I cloned the latest and tried running enumerate:
jb55@quiver> hwi enumerate
[
{
"path": "webusb:001:2",
"type": "trezor",
"error": "Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'."
}
]
so I tried promptpin:
jb55@quiver> hwi promptpin
{
"error": "You must specify a device type or fingerprint for all commands except enumerate",
"code": -1
}
not sure what to do from here
This is a bit of a thorny patch since each device does this differently and have weird quirks.
Would still be nice to try and do the single-key output case, "garden path".
The code here: https://github.com/achow101/HWI/blob/master/serializations.py#L663 is calling the dictionary keys "fingerprint", but they are actually individual pubkeys. Also, it doesn't actually put the fingerprint as the first value in the values array.
This is more of a documentation issue - the structure of the data is still correct, because fingerprints are 4 bytes. It's just confusing to the reader and assumes the caller knows to have the first array element set to the fingerprint.
In order for us to properly discard keys we've already seen when we have a mix of output types even if the imported descriptor is wpkh
, for example, we need combo support. While the "ISMINE" logic makes sure that all funds are seen during blockchain scan, related infrastructure such as CAffectedKeyVisitor
does not, meaning achow101/bitcoin@5958766 doesn't really work properly.
It's currently the first test that needs display interaction on the device, yet the most likely to fail.
E.g. rename to test_sign_big_tx
.
I have a strange behaviour:
./hwi.py -d "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS02@14200000/Nano S@14200000/Nano S@1/IOUSBHostHIDDevice@14200000,1" -t "ledger" getmasterxpub
{"error": "You must specify a device type or fingerprint for all commands except enumerate", "code": -1}
OS: 18.0.0 Darwin Kernel Version 18.0.0: Wed Aug 22 20:13:40 PDT 2018; root:xnu-4903.201.2~1/RELEASE_X86_64 x86_64
commit: b10bfe6
EDIT: curiously with "--fingerprint" it works.
I tried:
./run_tests.py --ledger
But in fact I had to use:
./run_tests.py --ledger --no_trezor --no_coldcard --no_keepkey --no_bitbox
bitcoin/bitcoin#15368 adds checksums for descriptors and requires them for using importmulti
. We need to make sure descriptors that we generate have checksums. It would also be nice for us to check that descriptors being input have valid checksums.
If I don't insert or unlock the device enumerate returns []
as expected. Once unlocked I get:
./hwi.py enumerate
INFO:trezorlib.transport:looking for device by full path: hid:IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS10@14600000/TREZOR@14600000/TREZOR Interface@1/IOUSBHostHIDDevice@14600000,1
[{"type": "trezor", "path": "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS10@14600000/TREZOR@14600000/TREZOR Interface@1/IOUSBHostHIDDevice@14600000,1", "serial_number": "#################", "error": "Could not open client or get fingerprint information: dlopen(libusb-1.0.dylib, 6): image not found"}]
# serial number censored by me
trezor-0.10.2 and hidapi 0.7.99.post2
If you install their bridge binary on linux(firmware update reasons!) I get this for enumerate:
[{"path": "bridge:lib01", "type": "trezor", "error": "Could not open client or get fingerprint information: trezord: acquire/lib01/null failed with code 400: wrong previous session"}, {"path": "webusb:001:1", "type": "trezor", "error": "Could not open client or get fingerprint information: LIBUSB_ERROR_BUSY [-6]"}]
This would allow Core importmulti to treat it as change rather than a receiving address.
Everything seems to work with the binary, but when I install from source using Poetry then
enumerate
returns an empty array. That suggest a silent failure somewhere. Any guesses?
$ poetry install
Creating virtualenv hwi-py3.6 in /home/dev/.cache/pypoetry/virtualenvs
Installing dependencies from lock file
Package operations: 14 installs, 0 updates, 0 removals
- Installing altgraph (0.16.1)
- Installing future (0.17.1)
- Installing macholib (1.11)
- Installing pbkdf2 (1.3)
- Installing pefile (2018.8.8)
- Installing typing (3.6.6)
- Installing ecdsa (0.13)
- Installing hidapi (0.7.99.post21)
- Installing libusb1 (1.7)
- Installing mnemonic (0.18)
- Installing pyaes (1.6.1)
- Installing pyinstaller (3.4)
- Installing python-bitcoinrpc (1.0)
- Installing typing-extensions (3.7.2)
- Installing hwi (1.0.0)
A setup.py file already exists. Using it.
Back in #74 the empty array was a result of a missing ecda
dependency, but that seems included above.
If you do pip3 uninstall mnemonic
then ./hwi.py enumerate
will return an empty array (at least with a Trezor).
$ poetry install
Installing dependencies from lock file
Nothing to install or update
- Installing hwi (1.0.0)
A setup.py file already exists. Using it.
$ ./hwi.py enumerate
Traceback (most recent call last):
File "./hwi.py", line 7, in <module>
main()
File "/home/torkel/dev/python/bitcoin-core-hwi/hwilib/cli.py", line 222, in main
result = process_commands(sys.argv[1:])
File "/home/torkel/dev/python/bitcoin-core-hwi/hwilib/cli.py", line 178, in process_commands
return args.func(args)
File "/home/torkel/dev/python/bitcoin-core-hwi/hwilib/cli.py", line 30, in enumerate_handler
return enumerate(password=args.password)
File "/home/torkel/dev/python/bitcoin-core-hwi/hwilib/commands.py", line 37, in enumerate
result.extend(imported_dev.enumerate(password))
File "/home/torkel/dev/python/bitcoin-core-hwi/hwilib/devices/ledger.py", line 338, in enumerate
for d in hid.enumerate(LEDGER_VENDOR_ID, LEDGER_DEVICE_ID):
NameError: name 'hid' is not defined
Adding import hid
at the top of hwilib/devices/ledger.py
fixes the issue.
Current syntax:
./hwi.py --fingerprint=00000000 displayaddress "m/84h/1h/0h/0/0" --wpkh
Preferred (alternative) syntax:
./hwi.py displayaddress "wpkh([00000000/84h/1h/0h]tpubDDUZ..../0/0)"
--fingerprint
is in the descriptor--wpkh
is specified by the descriptor and doesn't require very fancy parsing--testnet
: this can be inferred from the use of tpub
instead of xpub
, or from the coin type 1h
Rationale: wallets should be able to call this command in a standardised fashion, with as few parameters as needed. E.g. Bitcoin Core could be patched to call this command after you register ../HWI/hwi.py
as a signer for 00000000
and do bitcoin-cli -rpcwallet=hww signerdisplayaddress <address>
(where address
might be the result of bitcoin-cli getnewaddress
)
In the above example tpub
is redundant and may or may not be provided. So it's probably better to rely on the coin type to infer testnet. In addition, we could / should throw if an xpub is present but doesn't actually match, because that suggests some sort of data corruption.
./hwi.py enumerate
INFO:trezorlib.transport:looking for device by full path: hid:0001:0010:00
INFO:trezorlib.client:creating client instance for device: hid:0001:0010:00
INFO:trezorlib.transport:looking for device by full path: hid:0001:0010:01
Traceback (most recent call last):
File "./hwi.py", line 11, in <module>
result = process_commands(sys.argv[1:])
File "/home/greg/bitcoin-dev/HWI/hwilib/commands.py", line 223, in process_commands
return args.func()
File "/home/greg/bitcoin-dev/HWI/hwilib/commands.py", line 86, in enumerate
client = get_client(d_data['type'], d_data['path'])
File "/home/greg/bitcoin-dev/HWI/hwilib/commands.py", line 41, in get_client
client = trezori.TrezorClient(device=device, path=device_path)
File "/home/greg/bitcoin-dev/HWI/hwilib/trezori.py", line 55, in __init__
self.client = Trezor(transport=get_transport("hid:"+path.decode()))
File "/home/greg/.local/lib/python3.5/site-packages/trezorlib/transport/__init__.py", line 118, in get_transport
return transports[0].find_by_path(path, prefix_search=prefix_search)
File "/home/greg/.local/lib/python3.5/site-packages/trezorlib/transport/__init__.py", line 67, in find_by_path
raise TransportException('{} device not found: {}'.format(cls.PATH_PREFIX, path))
trezorlib.transport.TransportException: hid device not found: hid:0001:0010:01
On a Ubuntu 18.04 machine I installed Poetry using the magic albeit scary incantation here.
poetry install
[ImportError]
No module named pkg_resources
install [--no-dev] [--dry-run] [-E|--extras EXTRAS] [--develop DEVELOP]
The Trezor and Keepkey both require that the user respond to prompts and to enter things into the keyboard which are then sent to the device over USB. This is not ideal for us since HWI is intended to be a utility where everything the user needs to do is done in one command. I don't see any way around this though as all of the prompts require seeing something on the device's display which we are unable to see with software.
How should this be done?
I can think of two plausible ways:
Allow multiple devices to be specified and xpubs extracted in one call. This means keys are in one place, naturally which is a large downside.
Have users simply provide the xpubs when creating the importmulti descriptor import. Users can get the xpubs from different hosts in different locations, using the same command to ensure the same path and type. This is more manual, but allows keys to never be in the same place.
I prefer (2) for the security, though more onerous, and it's probably not plausible to have someone have 3+ devices plugged in at the same time anyways.
I was reading throught the code for importmulti, it seems that timestamp: "now"
starts scanning at the latest block? so rescans effectively don't happen? should timestamp be 0 or configurable?
HWI Commit:
5b82992 (HEAD -> master, origin/master, origin/HEAD)
`bitcoin-cli --version Bitcoin Core RPC client version v0.17.0.0-6ba1f1543``
When I try to produce the public keys to be imported into bitcoin-core with the command:
./hwi.py -t "ledger" -d "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS02@14200000/Nano S@14200000/Nano S@0/IOUSBHostHIDDevice@14200000,0" getkeypool --keypool 'm/84h/0h/0h/0' 0 2
[{"pubkeys": [{"02e37aadf09796cd325507c44598653bb52e9162b76ff0a469fda277d5c5001731": {"0000000000000000000000000000000053d7730a": "m/84h/0h/0h/0/0"}}], "scriptPubKey": {"address": "1PJ9TMFATQtAe7T4tVUa1Tk92YuCZhqUHp"}, "timestamp": "now", "internal": false, "keypool": true}, {"pubkeys": [{"03da4b53e2fe32695f379c7d95fa5ab9f317c9a3c48b98afa889749958a5c6939a": {"0000000000000000000000000000000053d7730a": "m/84h/0h/0h/0/1"}}], "scriptPubKey": {"address": "18ZkLiiuyZASYRzw6dK1tFTT738DVsb5Zm"}, "timestamp": "now", "internal": false, "keypool": true}, {"pubkeys": [{"02da481b2f95f25693588ef51ea4060ca5eee735e989ee826a90703a1d90e3df12": {"0000000000000000000000000000000053d7730a": "m/84h/0h/0h/0/2"}}], "scriptPubKey": {"address": "1HHgp1SZBVbDxSZnV5Wt1Ly7wMUJmUuzkP"}, "timestamp": "now", "internal": false, "keypool": true}]
Then importing into bitcoin-core...
bitcoin-cli -rpcwallet=test84 importmulti '[{"pubkeys": [{"02e37aadf09796cd325507c44598653bb52e9162b76ff0a469fda277d5c5001731": {"0000000000000000000000000000000053d7730a": "m/84h/0h/0h/0/0"}}], "scriptPubKey": {"address": "1PJ9TMFATQtAe7T4tVUa1Tk92YuCZhqUHp"}, "timestamp": "now", "internal": false, "keypool": true}, {"pubkeys": [{"03da4b53e2fe32695f379c7d95fa5ab9f317c9a3c48b98afa889749958a5c6939a": {"0000000000000000000000000000000053d7730a": "m/84h/0h/0h/0/1"}}], "scriptPubKey": {"address": "18ZkLiiuyZASYRzw6dK1tFTT738DVsb5Zm"}, "timestamp": "now", "internal": false, "keypool": true}, {"pubkeys": [{"02da481b2f95f25693588ef51ea4060ca5eee735e989ee826a90703a1d90e3df12": {"0000000000000000000000000000000053d7730a": "m/84h/0h/0h/0/2"}}], "scriptPubKey": {"address": "1HHgp1SZBVbDxSZnV5Wt1Ly7wMUJmUuzkP"}, "timestamp": "now", "internal": false, "keypool": true}]'
[
{
"success": false,
"error": {
"code": -1,
"message": "Missing required fields"
}
},
{
"success": false,
"error": {
"code": -1,
"message": "Missing required fields"
}
},
{
"success": false,
"error": {
"code": -1,
"message": "Missing required fields"
}
}
]
'
This can probably be easily emulated by re-importing a pubkey without the proper origin information, which will strip it the necessary information to identify it as the hww's.
This is actually two closely related issues. Both are on a Trezor T with default config and passphrase enabled.
Unlike Trezor One, TT has the ability to enter passphrase on device. So HWI's method of passing --password
argument doesn't work. Instead, every time you perform a password-protected action, the device shows a pop-up asking for the passphrase.
Issues:
hwi displayaddress
), you are asked for the passphrase twice.# in enumerate():
saved_state = client.client.state
# in TrezorClient.__init__:
self.client = Trezor(..., state=saved_state)
get-passphrase-state
command and then allow supplying that on repeated runs, sort of what you do with prompt-pin
/send-pin
Currently the Trezor implementation does not support signing multisig inputs. It would be nice if it did.
If I only install hidapi
and btchip-python
and then call ./hwi enumerate
it returns []
. Once I add trezor[hidapi]
, keepkey
and pyaes
it works (ckcc-protocol
wasn't needed). Not sure which one did the trick. This stuff is easiest to test with pyenv
. I tested with Python 3.4.9.
E.g.:
./configure --with-miniupnpc=no --without-gui --disable-zmq --disable-tests --disable-bench
This includes --disable-gui
in case someone runs this on a machine that has QT dependencies.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.