diff --git a/hwilib/cli.py b/hwilib/cli.py index fd25d2a31..f0c352523 100644 --- a/hwilib/cli.py +++ b/hwilib/cli.py @@ -5,6 +5,7 @@ displayaddress, enumerate, find_device, + get_client_class, get_client, getmasterxpub, getxpub, @@ -22,6 +23,7 @@ ) from .errors import ( handle_errors, + BAD_ARGUMENT, DEVICE_CONN_ERROR, HELP_TEXT, MISSING_ARGUMENTS, @@ -88,6 +90,10 @@ def send_pin_handler(args, client): def install_udev_rules_handler(args): return install_udev_rules('udev', args.location) +def getfeatures_handler(args): + client_class = get_client_class(args.device_type) + return client_class.get_features() + class HWIHelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): pass @@ -208,6 +214,9 @@ def process_commands(cli_args): sendpin_parser.add_argument('pin', help='The numeric positions of the PIN') sendpin_parser.set_defaults(func=send_pin_handler) + getfeatures_parser = subparsers.add_parser('getfeatures', help='Returns the supported features for the given device type') + getfeatures_parser.set_defaults(func=getfeatures_handler) + if sys.platform.startswith("linux"): udevrules_parser = subparsers.add_parser('installudevrules', help='Install and load the udev rule files for the hardware wallet devices') udevrules_parser.add_argument('--location', help='The path where the udev rules files will be copied', default='/etc/udev/rules.d/') @@ -254,6 +263,14 @@ def process_commands(cli_args): result = args.func(args) return result + # Do get features + if command == 'getfeatures': + if not args.device_type: + return {'error': 'Device type needs to be specified to get features', 'code': BAD_ARGUMENT} + with handle_errors(result=result, debug=args.debug): + result = args.func(args) + return result + # Auto detect if we are using fingerprint or type to identify device if args.fingerprint or (args.device_type and not args.device_path): client = find_device(args.password, args.device_type, args.fingerprint, args.expert) diff --git a/hwilib/commands.py b/hwilib/commands.py index 54b41150f..c3baf2c8e 100644 --- a/hwilib/commands.py +++ b/hwilib/commands.py @@ -43,21 +43,32 @@ class AddressType(Enum): WPKH = 2 SH_WPKH = 3 +def get_client_class(device_type): + device_type_split = device_type.split('_') + if device_type_split[-1].lower() == 'simulator': + del device_type_split[-1] + device_type_split = [x.capitalize() for x in device_type_split] + device_model = ''.join(device_type_split) + module = device_type_split[0].lower() + + try: + imported_dev = importlib.import_module('.devices.' + module, __package__) + client_constructor = getattr(imported_dev, device_model + 'Client') + except (ImportError, AttributeError): + raise UnknownDeviceError('Unknown device type specified') + + return client_constructor + # Get the client for the device def get_client(device_type, device_path, password='', expert=False): - device_type = device_type.split('_')[0] - class_name = device_type.capitalize() - module = device_type.lower() - client = None try: - imported_dev = importlib.import_module('.devices.' + module, __package__) - client_constructor = getattr(imported_dev, class_name + 'Client') + client_constructor = get_client_class(device_type) client = client_constructor(device_path, password, expert) - except ImportError: + except: if client: client.close() - raise UnknownDeviceError('Unknown device type specified') + raise return client @@ -81,7 +92,7 @@ def find_device(password='', device_type=None, fingerprint=None, expert=False): continue client = None try: - client = get_client(d['type'], d['path'], password, expert) + client = get_client(d['model'], d['path'], password, expert) if fingerprint: master_fpr = d.get('fingerprint', None) diff --git a/hwilib/devices/coldcard.py b/hwilib/devices/coldcard.py index a196f90d9..28bd0936a 100644 --- a/hwilib/devices/coldcard.py +++ b/hwilib/devices/coldcard.py @@ -2,7 +2,11 @@ from typing import Dict, Union -from ..hwwclient import HardwareWalletClient +from ..hwwclient import ( + DeviceFeature, + HardwareWalletClient, + SupportedFeatures, +) from ..errors import ( ActionCanceledError, BadArgumentError, @@ -94,6 +98,29 @@ def func(*args, **kwargs): # This class extends the HardwareWalletClient for ColdCard specific things class ColdcardClient(HardwareWalletClient): + # Setup features + features = SupportedFeatures() + features.getxpub = DeviceFeature.SUPPORTED + features.signmessage = DeviceFeature.SUPPORTED + features.setup = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.wipe = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.recover = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.backup = DeviceFeature.SUPPORTED + features.sign_p2pkh = DeviceFeature.SUPPORTED + features.sign_p2sh_p2wpkh = DeviceFeature.SUPPORTED + features.sign_p2wpkh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_coinjoin = DeviceFeature.SUPPORTED + features.sign_mixed_segwit = DeviceFeature.SUPPORTED + features.display_address = DeviceFeature.SUPPORTED + def __init__(self, path, password='', expert=False): super(ColdcardClient, self).__init__(path, password, expert) # Simulator hard coded pipe socket @@ -340,6 +367,11 @@ def send_pin(self, pin): def toggle_passphrase(self): raise UnavailableActionError('The Coldcard does not support toggling passphrase from the host') + # Get HWI features for this device + @classmethod + def get_features(self): + return self.features.get_printable_dict() + def enumerate(password=''): results = [] devices = hid.enumerate(COINKITE_VID, CKCC_PID) diff --git a/hwilib/devices/digitalbitbox.py b/hwilib/devices/digitalbitbox.py index 466430d45..73222d702 100644 --- a/hwilib/devices/digitalbitbox.py +++ b/hwilib/devices/digitalbitbox.py @@ -15,7 +15,11 @@ import time from typing import Dict, Union -from ..hwwclient import HardwareWalletClient +from ..hwwclient import ( + DeviceFeature, + HardwareWalletClient, + SupportedFeatures, +) from ..errors import ( ActionCanceledError, BadArgumentError, @@ -326,6 +330,29 @@ def format_backup_filename(name): # This class extends the HardwareWalletClient for Digital Bitbox specific things class DigitalbitboxClient(HardwareWalletClient): + # Setup features + features = SupportedFeatures() + features.getxpub = DeviceFeature.SUPPORTED + features.signmessage = DeviceFeature.SUPPORTED + features.setup = DeviceFeature.SUPPORTED + features.wipe = DeviceFeature.SUPPORTED + features.recover = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.backup = DeviceFeature.SUPPORTED + features.sign_p2pkh = DeviceFeature.SUPPORTED + features.sign_p2sh_p2wpkh = DeviceFeature.SUPPORTED + features.sign_p2wpkh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_bare = DeviceFeature.SUPPORTED + features.sign_arbitrary_bare = DeviceFeature.SUPPORTED + features.sign_arbitrary_p2sh = DeviceFeature.SUPPORTED + features.sign_arbitrary_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_arbitrary_p2wsh = DeviceFeature.SUPPORTED + features.sign_coinjoin = DeviceFeature.SUPPORTED + features.sign_mixed_segwit = DeviceFeature.SUPPORTED + features.display_address = DeviceFeature.FIRMWARE_NOT_SUPPORTED + def __init__(self, path, password, expert=False): super(DigitalbitboxClient, self).__init__(path, password, expert) if not password: @@ -617,6 +644,16 @@ def send_pin(self, pin): def toggle_passphrase(self): raise UnavailableActionError('The Digital Bitbox does not support toggling passphrase from the host') + # Get HWI features for this device + @classmethod + def get_features(self): + return self.features.get_printable_dict() + +class Digitalbitbox01Client(DigitalbitboxClient): + def __init__(self, path, password='', expert=False): + super(Digitalbitbox01Client, self).__init__(path, password, expert) + self.type = 'Digital BitBox01' + def enumerate(password=''): results = [] devices = hid.enumerate(DBB_VENDOR_ID, DBB_DEVICE_ID) diff --git a/hwilib/devices/keepkey.py b/hwilib/devices/keepkey.py index ab69ef6fc..507118f94 100644 --- a/hwilib/devices/keepkey.py +++ b/hwilib/devices/keepkey.py @@ -6,6 +6,10 @@ common_err_msgs, handle_errors, ) +from ..hwwclient import ( + DeviceFeature, + SupportedFeatures, +) from .trezorlib.transport import ( enumerate_devices, KEEPKEY_VENDOR_IDS, @@ -15,10 +19,38 @@ py_enumerate = enumerate # Need to use the enumerate built-in but there's another function already named that class KeepkeyClient(TrezorClient): + + # Setup features + features = SupportedFeatures() + features.getxpub = DeviceFeature.SUPPORTED + features.signmessage = DeviceFeature.SUPPORTED + features.setup = DeviceFeature.SUPPORTED + features.wipe = DeviceFeature.SUPPORTED + features.recover = DeviceFeature.SUPPORTED + features.backup = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_p2pkh = DeviceFeature.SUPPORTED + features.sign_p2sh_p2wpkh = DeviceFeature.SUPPORTED + features.sign_p2wpkh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_coinjoin = DeviceFeature.SUPPORTED + features.sign_mixed_segwit = DeviceFeature.SUPPORTED + features.display_address = DeviceFeature.SUPPORTED + def __init__(self, path, password='', expert=False): super(KeepkeyClient, self).__init__(path, password, expert) self.type = 'Keepkey' + @classmethod + def get_features(self): + return self.features.get_printable_dict() + def enumerate(password=''): results = [] for dev in enumerate_devices(): diff --git a/hwilib/devices/ledger.py b/hwilib/devices/ledger.py index 3880ccee9..589a1f123 100644 --- a/hwilib/devices/ledger.py +++ b/hwilib/devices/ledger.py @@ -2,7 +2,11 @@ from typing import Dict, Union -from ..hwwclient import HardwareWalletClient +from ..hwwclient import ( + DeviceFeature, + HardwareWalletClient, + SupportedFeatures, +) from ..errors import ( ActionCanceledError, BadArgumentError, @@ -100,8 +104,32 @@ def func(*args, **kwargs): # This class extends the HardwareWalletClient for Ledger Nano S and Nano X specific things class LedgerClient(HardwareWalletClient): + # Setup features + features = SupportedFeatures() + features.getxpub = DeviceFeature.SUPPORTED + features.signmessage = DeviceFeature.SUPPORTED + features.setup = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.wipe = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.recover = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.backup = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_p2pkh = DeviceFeature.SUPPORTED + features.sign_p2sh_p2wpkh = DeviceFeature.SUPPORTED + features.sign_p2wpkh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_bare = DeviceFeature.SUPPORTED + features.sign_arbitrary_bare = DeviceFeature.SUPPORTED + features.sign_arbitrary_p2sh = DeviceFeature.SUPPORTED + features.sign_arbitrary_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_arbitrary_p2wsh = DeviceFeature.SUPPORTED + features.sign_coinjoin = DeviceFeature.SUPPORTED + features.sign_mixed_segwit = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.display_address = DeviceFeature.SUPPORTED + def __init__(self, path, password='', expert=False): super(LedgerClient, self).__init__(path, password, expert) + self.type = 'Ledger Nano S and X' if path.startswith('tcp'): split_path = path.split(':') @@ -356,19 +384,19 @@ def display_address(self, keypath, p2sh_p2wpkh, bech32, redeem_script=None, desc # Setup a new device def setup_device(self, label='', passphrase=''): - raise UnavailableActionError('The Ledger Nano S and X do not support software setup') + raise UnavailableActionError('The {} does not support software setup'.format(self.type)) # Wipe this device def wipe_device(self): - raise UnavailableActionError('The Ledger Nano S and X do not support wiping via software') + raise UnavailableActionError('The {} does not support wiping via software'.format(self.type)) # Restore device from mnemonic or xprv def restore_device(self, label='', word_count=24): - raise UnavailableActionError('The Ledger Nano S and X do not support restoring via software') + raise UnavailableActionError('The {} does not support restoring via software'.format(self.type)) # Begin backup process def backup_device(self, label='', passphrase=''): - raise UnavailableActionError('The Ledger Nano S and X do not support creating a backup via software') + raise UnavailableActionError('The {} does not support creating a backup via software'.format(self.type)) # Close the device def close(self): @@ -376,16 +404,31 @@ def close(self): # Prompt pin def prompt_pin(self): - raise UnavailableActionError('The Ledger Nano S and X do not need a PIN sent from the host') + raise UnavailableActionError('The {} does not need a PIN sent from the host'.format(self.type)) # Send pin def send_pin(self, pin): - raise UnavailableActionError('The Ledger Nano S and X do not need a PIN sent from the host') + raise UnavailableActionError('The {} does not need a PIN sent from the host'.format(self.type)) # Toggle passphrase def toggle_passphrase(self): raise UnavailableActionError('The Ledger Nano S and X do not support toggling passphrase from the host') + # Get HWI features for this device + @classmethod + def get_features(self): + return self.features.get_printable_dict() + +class LedgerNanoSClient(LedgerClient): + def __init__(self, path, password='', expert=False): + super(LedgerNanoSClient, self).__init__(path, password, expert) + self.type = 'Ledger Nano S' + +class LedgerNanoXClient(LedgerClient): + def __init__(self, path, password='', expert=False): + super(LedgerNanoXClient, self).__init__(path, password, expert) + self.type = 'Ledger Nano X' + def enumerate(password=''): results = [] devices = [] diff --git a/hwilib/devices/trezor.py b/hwilib/devices/trezor.py index 6c73aab57..24155868f 100644 --- a/hwilib/devices/trezor.py +++ b/hwilib/devices/trezor.py @@ -2,7 +2,11 @@ from typing import Dict, Union -from ..hwwclient import HardwareWalletClient +from ..hwwclient import ( + DeviceFeature, + HardwareWalletClient, + SupportedFeatures, +) from ..errors import ( ActionCanceledError, BadArgumentError, @@ -551,6 +555,77 @@ def toggle_passphrase(self): print(PIN_MATRIX_DESCRIPTION, file=sys.stderr) return {'success': True} + # Get HWI features for this device + @classmethod + def get_features(self): + raise UnavailableActionError('A specific Trezor model must be specified to get the features') + +class Trezor1Client(TrezorClient): + + # Setup features + features = SupportedFeatures() + features.getxpub = DeviceFeature.SUPPORTED + features.signmessage = DeviceFeature.SUPPORTED + features.setup = DeviceFeature.SUPPORTED + features.wipe = DeviceFeature.SUPPORTED + features.recover = DeviceFeature.SUPPORTED + features.backup = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_p2pkh = DeviceFeature.SUPPORTED + features.sign_p2sh_p2wpkh = DeviceFeature.SUPPORTED + features.sign_p2wpkh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_coinjoin = DeviceFeature.SUPPORTED + features.sign_mixed_segwit = DeviceFeature.SUPPORTED + features.display_address = DeviceFeature.SUPPORTED + + def __init__(self, path, password='', expert=False): + super(Trezor1Client, self).__init__(path, password, expert) + self.type = 'Trezor 1' + + @classmethod + def get_features(self): + return self.features.get_printable_dict() + +class TrezorTClient(TrezorClient): + + # Setup features + features = SupportedFeatures() + features.getxpub = DeviceFeature.SUPPORTED + features.signmessage = DeviceFeature.SUPPORTED + features.setup = DeviceFeature.SUPPORTED + features.wipe = DeviceFeature.SUPPORTED + features.recover = DeviceFeature.SUPPORTED + features.backup = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_p2pkh = DeviceFeature.SUPPORTED + features.sign_p2sh_p2wpkh = DeviceFeature.SUPPORTED + features.sign_p2wpkh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh = DeviceFeature.SUPPORTED + features.sign_multi_p2sh_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_p2wsh = DeviceFeature.SUPPORTED + features.sign_multi_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_bare = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2sh_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_arbitrary_p2wsh = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.sign_coinjoin = DeviceFeature.SUPPORTED + features.sign_mixed_segwit = DeviceFeature.FIRMWARE_NOT_SUPPORTED + features.display_address = DeviceFeature.SUPPORTED + + def __init__(self, path, password='', expert=False): + super(TrezorTClient, self).__init__(path, password, expert) + self.type = 'Trezor T' + + @classmethod + def get_features(self): + return self.features.get_printable_dict() + def enumerate(password=''): results = [] for dev in enumerate_devices(): diff --git a/hwilib/hwwclient.py b/hwilib/hwwclient.py index e27db11f9..6ca4ca9e2 100644 --- a/hwilib/hwwclient.py +++ b/hwilib/hwwclient.py @@ -4,6 +4,60 @@ from .descriptor import Descriptor from .serializations import PSBT +from enum import IntEnum + +class DeviceFeature(IntEnum): + SUPPORTED = 1 # The device supports the feature and so does HWI + NOT_SUPPORTED = 2 # The device supports the feature but HWI has not implemented it yet + FIRMWARE_NOT_SUPPORTED = 3 # The firmware does not support the feature so HWI cannot + +class SupportedFeatures(object): + + def __init__(self) -> None: + self.getxpub = DeviceFeature.NOT_SUPPORTED + self.signmessage = DeviceFeature.NOT_SUPPORTED + self.setup = DeviceFeature.NOT_SUPPORTED + self.wipe = DeviceFeature.NOT_SUPPORTED + self.recover = DeviceFeature.NOT_SUPPORTED + self.backup = DeviceFeature.NOT_SUPPORTED + self.sign_p2pkh = DeviceFeature.NOT_SUPPORTED + self.sign_p2sh_p2wpkh = DeviceFeature.NOT_SUPPORTED + self.sign_p2wpkh = DeviceFeature.NOT_SUPPORTED + self.sign_multi_p2sh = DeviceFeature.NOT_SUPPORTED + self.sign_multi_p2sh_p2wsh = DeviceFeature.NOT_SUPPORTED + self.sign_multi_p2wsh = DeviceFeature.NOT_SUPPORTED + self.sign_multi_bare = DeviceFeature.NOT_SUPPORTED + self.sign_arbitrary_bare = DeviceFeature.NOT_SUPPORTED + self.sign_arbitrary_p2sh = DeviceFeature.NOT_SUPPORTED + self.sign_arbitrary_p2sh_p2wsh = DeviceFeature.NOT_SUPPORTED + self.sign_arbitrary_p2wsh = DeviceFeature.NOT_SUPPORTED + self.sign_coinjoin = DeviceFeature.NOT_SUPPORTED + self.sign_mixed_segwit = DeviceFeature.NOT_SUPPORTED + self.display_address = DeviceFeature.NOT_SUPPORTED + + def get_printable_dict(self) -> Dict[str, DeviceFeature]: + d = {} + d['getxpub'] = self.getxpub + d['signmessage'] = self.signmessage + d['setup'] = self.setup + d['wipe'] = self.wipe + d['recover'] = self.recover + d['backup'] = self.backup + d['sign_p2pkh'] = self.sign_p2pkh + d['sign_p2sh_p2wpkh'] = self.sign_p2sh_p2wpkh + d['sign_p2wpkh'] = self.sign_p2wpkh + d['sign_multi_p2sh'] = self.sign_multi_p2sh + d['sign_multi_p2sh_p2wsh'] = self.sign_multi_p2sh_p2wsh + d['sign_multi_p2wsh'] = self.sign_multi_p2wsh + d['sign_multi_bare'] = self.sign_multi_bare + d['sign_arbitrary_bare'] = self.sign_arbitrary_bare + d['sign_arbitrary_p2sh'] = self.sign_arbitrary_p2sh + d['sign_arbitrary_p2sh_p2wsh'] = self.sign_arbitrary_p2sh_p2wsh + d['sign_arbitrary_p2wsh'] = self.sign_arbitrary_p2wsh + d['sign_coinjoin'] = self.sign_coinjoin + d['sign_mixed_segwit'] = self.sign_mixed_segwit + d['display_address'] = self.display_address + return d class HardwareWalletClient(object): """Create a client for a HID device that has already been opened. @@ -185,3 +239,13 @@ def toggle_passphrase(self) -> Dict[str, Union[bool, str, int]]: """ raise NotImplementedError("The HardwareWalletClient base class " "does not implement this method") + + @classmethod + def get_features(self) -> 'SupportedFeatures': + """ + Get features. + + Returns an object with a listing of the features supported by this device. + """ + raise NotImplementedError("The HardwareWalletClient base class " + "does not implement this method") diff --git a/test/test_ledger.py b/test/test_ledger.py index 815952123..ee94b8105 100755 --- a/test/test_ledger.py +++ b/test/test_ledger.py @@ -63,41 +63,41 @@ def test_pin(self): result = self.do_command(self.dev_args + ['promptpin']) self.assertIn('error', result) self.assertIn('code', result) - self.assertEqual(result['error'], 'The Ledger Nano S and X do not need a PIN sent from the host') + self.assertEqual(result['error'], 'The Ledger Nano S does not need a PIN sent from the host') self.assertEqual(result['code'], -9) result = self.do_command(self.dev_args + ['sendpin', '1234']) self.assertIn('error', result) self.assertIn('code', result) - self.assertEqual(result['error'], 'The Ledger Nano S and X do not need a PIN sent from the host') + self.assertEqual(result['error'], 'The Ledger Nano S does not need a PIN sent from the host') self.assertEqual(result['code'], -9) def test_setup(self): result = self.do_command(self.dev_args + ['-i', 'setup']) self.assertIn('error', result) self.assertIn('code', result) - self.assertEqual(result['error'], 'The Ledger Nano S and X do not support software setup') + self.assertEqual(result['error'], 'The Ledger Nano S does not support software setup') self.assertEqual(result['code'], -9) def test_wipe(self): result = self.do_command(self.dev_args + ['wipe']) self.assertIn('error', result) self.assertIn('code', result) - self.assertEqual(result['error'], 'The Ledger Nano S and X do not support wiping via software') + self.assertEqual(result['error'], 'The Ledger Nano S does not support wiping via software') self.assertEqual(result['code'], -9) def test_restore(self): result = self.do_command(self.dev_args + ['-i', 'restore']) self.assertIn('error', result) self.assertIn('code', result) - self.assertEqual(result['error'], 'The Ledger Nano S and X do not support restoring via software') + self.assertEqual(result['error'], 'The Ledger Nano S does not support restoring via software') self.assertEqual(result['code'], -9) def test_backup(self): result = self.do_command(self.dev_args + ['backup']) self.assertIn('error', result) self.assertIn('code', result) - self.assertEqual(result['error'], 'The Ledger Nano S and X do not support creating a backup via software') + self.assertEqual(result['error'], 'The Ledger Nano S does not support creating a backup via software') self.assertEqual(result['code'], -9) class TestLedgerGetXpub(DeviceTestCase):