displayaddress: descriptor support, make --path a named argument

This commit is contained in:
Sjors Provoost 2019-02-13 15:47:32 +01:00
parent 163f926d52
commit a71658cf6d
No known key found for this signature in database
GPG Key ID: 57FF9BDBCC301009
4 changed files with 69 additions and 15 deletions

View File

@ -22,7 +22,7 @@ def backup_device_handler(args, client):
return backup_device(client, label=args.label, backup_passphrase=args.backup_passphrase)
def displayaddress_handler(args, client):
return displayaddress(client, path=args.path, sh_wpkh=args.sh_wpkh, wpkh=args.wpkh)
return displayaddress(client, desc=args.desc, path=args.path, sh_wpkh=args.sh_wpkh, wpkh=args.wpkh)
def enumerate_handler(args):
return enumerate(password=args.password)
@ -102,7 +102,9 @@ def process_commands(args):
getkeypool_parser.set_defaults(func=getkeypool_handler)
displayaddr_parser = subparsers.add_parser('displayaddress', help='Display an address')
displayaddr_parser.add_argument('path', help='The BIP 32 derivation path of the key embedded in the address')
group = displayaddr_parser.add_mutually_exclusive_group(required=True)
group.add_argument('--desc', help='Output Descriptor. E.g. wpkh([00000000/84h/0h/0h]xpub.../0/0), where 00000000 must match --fingerprint and xpub can be obtained with getxpub. See doc/descriptors.md in Bitcoin Core')
group.add_argument('--path', help='The BIP 32 derivation path of the key embedded in the address, default follows BIP43 convention, e.g. m/84h/0h/0h/1/*')
displayaddr_parser.add_argument('--sh_wpkh', action='store_true', help='Display the p2sh-nested segwit address associated with this key path')
displayaddr_parser.add_argument('--wpkh', action='store_true', help='Display the bech32 version of the address associated with this key path')
displayaddr_parser.set_defaults(func=displayaddress_handler)

View File

@ -6,9 +6,10 @@ import glob
import importlib
from .serializations import PSBT, Base64ToHex, HexToBase64, hash160
from .base58 import get_xpub_fingerprint_as_id, get_xpub_fingerprint_hex
from .base58 import get_xpub_fingerprint_as_id, get_xpub_fingerprint_hex, xpub_to_pub_hex
from os.path import dirname, basename, isfile
from .errors import NoPasswordError, UnavailableActionError, DeviceAlreadyInitError, DeviceAlreadyUnlockedError, UnknownDeviceError, BAD_ARGUMENT, NOT_IMPLEMENTED
from .descriptor import Descriptor
# Get the client for the device
def get_client(device_type, device_path, password=''):
@ -155,10 +156,25 @@ def getkeypool(client, path, start, end, internal=False, keypool=False, account=
import_data.append(this_import)
return import_data
def displayaddress(client, path, sh_wpkh=False, wpkh=False):
if sh_wpkh == True and wpkh == True:
return {'error':'Both `--wpkh` and `--sh_wpkh` can not be selected at the same time.','code':BAD_ARGUMENT}
return client.display_address(path, sh_wpkh, wpkh)
def displayaddress(client, path=None, desc=None, sh_wpkh=False, wpkh=False):
if path is not None:
if sh_wpkh == True and wpkh == True:
return {'error':'Both `--wpkh` and `--sh_wpkh` can not be selected at the same time.','code':BAD_ARGUMENT}
return client.display_address(path, sh_wpkh, wpkh)
elif desc is not None:
if sh_wpkh == True or wpkh == True:
return {'error':' `--wpkh` and `--sh_wpkh` can not be combined with --desc','code':BAD_ARGUMENT}
descriptor = Descriptor.parse(desc, client.is_testnet)
if descriptor is None:
return {'error':'Unable to parse descriptor: ' + desc,'code':BAD_ARGUMENT}
if descriptor.m_path is None:
return {'error':'Descriptor missing origin info: ' + desc,'code':BAD_ARGUMENT}
if descriptor.origin_fingerprint != client.fingerprint:
return {'error':'Descriptor fingerprint does not match device: ' + desc,'code':BAD_ARGUMENT}
xpub = client.get_pubkey_at_path(descriptor.m_path_base)['xpub']
if descriptor.base_key != xpub and descriptor.base_key != xpub_to_pub_hex(xpub):
return {'error':'Key in descriptor does not match device: ' + desc,'code':BAD_ARGUMENT}
return client.display_address(descriptor.m_path, descriptor.sh_wpkh, descriptor.wpkh)
def setup_device(client, label='', backup_passphrase=''):
return client.setup_device(label, backup_passphrase)

View File

@ -9,6 +9,7 @@ import time
import unittest
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from hwilib.base58 import xpub_to_pub_hex
from hwilib.cli import process_commands
from hwilib.serializations import PSBT
@ -391,20 +392,55 @@ class TestDisplayAddress(DeviceTestCase):
self.emulator.stop()
def test_display_address_bad_args(self):
result = process_commands(self.dev_args + ['displayaddress', '--sh_wpkh', '--wpkh', 'm/49h/1h/0h/0/0'])
result = process_commands(self.dev_args + ['displayaddress', '--sh_wpkh', '--wpkh', '--path', 'm/49h/1h/0h/0/0'])
self.assertIn('error', result)
self.assertIn('code', result)
self.assertEqual(result['code'], -7)
def test_display_address(self):
process_commands(self.dev_args + ['displayaddress', 'm/44h/1h/0h/0/0'])
process_commands(self.dev_args + ['displayaddress', '--sh_wpkh', 'm/49h/1h/0h/0/0'])
process_commands(self.dev_args + ['displayaddress', '--wpkh', 'm/84h/1h/0h/0/0'])
def test_display_address_path(self):
process_commands(self.dev_args + ['displayaddress', '--path', 'm/44h/1h/0h/0/0'])
process_commands(self.dev_args + ['displayaddress', '--sh_wpkh', '--path', 'm/49h/1h/0h/0/0'])
process_commands(self.dev_args + ['displayaddress', '--wpkh', '--path', 'm/84h/1h/0h/0/0'])
def test_bad_path(self):
result = process_commands(self.dev_args + ['displayaddress', 'f'])
def test_display_address_bad_path(self):
result = process_commands(self.dev_args + ['displayaddress', '--path', 'f'])
self.assertEquals(result['code'], -7)
def test_display_address_descriptor(self):
account_xpub = process_commands(self.dev_args + ['getxpub', 'm/84h/1h/0h'])['xpub']
p2sh_segwit_account_xpub = process_commands(self.dev_args + ['getxpub', 'm/49h/1h/0h'])['xpub']
legacy_account_xpub = process_commands(self.dev_args + ['getxpub', 'm/44h/1h/0h'])['xpub']
# Native SegWit address using xpub:
process_commands(self.dev_args + ['displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h)]' + account_xpub + '/0/0)'])
# Native SegWit address using hex encoded pubkey:
process_commands(self.dev_args + ['displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h)]' + xpub_to_pub_hex(account_xpub) + '/0/0)'])
# P2SH wrapped SegWit address using xpub:
process_commands(self.dev_args + ['displayaddress', '--desc', 'sh(wpkh([' + self.fingerprint + '/49h/1h/0h)]' + p2sh_segwit_account_xpub + '/0/0))'])
# Legacy address
process_commands(self.dev_args + ['displayaddress', '--desc', 'pkh([' + self.fingerprint + '/44h/1h/0h)]' + legacy_account_xpub + '/0/0)'])
# Should check xpub
result = process_commands(self.dev_args + ['displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h)]' + "not_and_xpub" + '/0/0)'])
self.assertIn('error', result)
self.assertIn('code', result)
self.assertEqual(result['code'], -7)
# Should check hex pub
result = process_commands(self.dev_args + ['displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h)]' + "not_and_xpub" + '/0/0)'])
self.assertIn('error', result)
self.assertIn('code', result)
self.assertEqual(result['code'], -7)
# Should check fingerprint
process_commands(self.dev_args + ['displayaddress', '--desc', 'wpkh([00000000/84h/1h/0h)]' + account_xpub + '/0/0)'])
self.assertIn('error', result)
self.assertIn('code', result)
self.assertEqual(result['code'], -7)
class TestSignMessage(DeviceTestCase):
def setUp(self):
self.emulator.start()

View File

@ -64,7 +64,7 @@ def digitalbitbox_test_suite(rpc, userpass, simulator):
self.assertEqual(result['code'], -9)
def test_display(self):
result = process_commands(self.dev_args + ['displayaddress', 'm/0h'])
result = process_commands(self.dev_args + ['displayaddress', '--path', 'm/0h'])
self.assertIn('error', result)
self.assertIn('code', result)
self.assertEqual(result['error'], 'The Digital Bitbox does not have a screen to display addresses on')