Skip to content

Commit

Permalink
Add enumerate --no-emulators option to skip enumerating emulators
Browse files Browse the repository at this point in the history
  • Loading branch information
achow101 committed Mar 26, 2024
1 parent 8d4fa19 commit 217baa1
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 30 deletions.
3 changes: 2 additions & 1 deletion hwilib/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def displayaddress_handler(args: argparse.Namespace, client: HardwareWalletClien
return displayaddress(client, desc=args.desc, path=args.path, addr_type=args.addr_type)

def enumerate_handler(args: argparse.Namespace) -> List[Dict[str, Any]]:
return enumerate(password=args.password, expert=args.expert, chain=args.chain)
return enumerate(password=args.password, expert=args.expert, chain=args.chain, allow_emulators=args.allow_emulators)

def getmasterxpub_handler(args: argparse.Namespace, client: HardwareWalletClient) -> Dict[str, str]:
return getmasterxpub(client, addrtype=args.addr_type, account=args.account)
Expand Down Expand Up @@ -151,6 +151,7 @@ def get_parser() -> HWIArgumentParser:
subparsers.required = True

enumerate_parser = subparsers.add_parser('enumerate', help='List all available devices')
enumerate_parser.add_argument("--no-emulators", help="Disable enumeration of device emulators", action="store_false", dest="allow_emulators")
enumerate_parser.set_defaults(func=enumerate_handler)

getmasterxpub_parser = subparsers.add_parser('getmasterxpub', help='Get the extended public key for BIP 44 standard derivation paths. Convenience function to get xpubs given the address type, account, and chain type.')
Expand Down
4 changes: 2 additions & 2 deletions hwilib/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def get_client(device_type: str, device_path: str, password: Optional[str] = Non
return client

# Get a list of all available hardware wallets
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
"""
Enumerate all of the devices that HWI can potentially access.
Expand All @@ -114,7 +114,7 @@ def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain
for module in all_devs:
try:
imported_dev = importlib.import_module('.devices.' + module, __package__)
result.extend(imported_dev.enumerate(password, expert, chain))
result.extend(imported_dev.enumerate(password, expert, chain, allow_emulators))
except ImportError as e:
# Warn for ImportErrors, but largely ignore them to allow users not install
# all device dependencies if only one or some devices are wanted.
Expand Down
2 changes: 1 addition & 1 deletion hwilib/devices/bitbox02.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def _xpubs_equal_ignoring_version(xpub1: bytes, xpub2: bytes) -> bool:
return xpub1[4:] == xpub2[4:]


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
"""
Enumerate all BitBox02 devices. Bootloaders excluded.
"""
Expand Down
5 changes: 3 additions & 2 deletions hwilib/devices/coldcard.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,10 +399,11 @@ def can_sign_taproot(self) -> bool:
return False


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
results = []
devices = hid.enumerate(COINKITE_VID, CKCC_PID)
devices.append({'path': CC_SIMULATOR_SOCK.encode()})
if allow_emulators:
devices.append({'path': CC_SIMULATOR_SOCK.encode()})
for d in devices:
d_data: Dict[str, Any] = {}

Expand Down
17 changes: 9 additions & 8 deletions hwilib/devices/digitalbitbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,17 +679,18 @@ def can_sign_taproot(self) -> bool:
return False


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
results = []
devices = hid.enumerate(DBB_VENDOR_ID, DBB_DEVICE_ID)
# Try connecting to simulator
try:
dev = BitboxSimulator('127.0.0.1', 35345)
dev.send_recv(b'{"device" : "info"}')
devices.append({'path': b'udp:127.0.0.1:35345', 'interface_number': 0})
dev.close()
except Exception:
pass
if allow_emulators:
try:
dev = BitboxSimulator('127.0.0.1', 35345)
dev.send_recv(b'{"device" : "info"}')
devices.append({'path': b'udp:127.0.0.1:35345', 'interface_number': 0})
dev.close()
except Exception:
pass
for d in devices:
if ('interface_number' in d and d['interface_number'] == 0
or ('usage_page' in d and d['usage_page'] == 0xffff)):
Expand Down
21 changes: 11 additions & 10 deletions hwilib/devices/jade.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ def can_sign_taproot(self) -> bool:
return False


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
results = []

def _get_device_entry(device_model: str, device_path: str) -> Dict[str, Any]:
Expand Down Expand Up @@ -537,16 +537,17 @@ def _get_device_entry(device_model: str, device_path: str) -> Dict[str, Any]:
results.append(_get_device_entry('jade', devinfo.device))

# If we can connect to the simulator, add it too
try:
with JadeAPI.create_serial(SIMULATOR_PATH, timeout=1) as jade:
verinfo = jade.get_version_info()
if allow_emulators:
try:
with JadeAPI.create_serial(SIMULATOR_PATH, timeout=1) as jade:
verinfo = jade.get_version_info()

if verinfo is not None:
results.append(_get_device_entry('jade_simulator', SIMULATOR_PATH))
if verinfo is not None:
results.append(_get_device_entry('jade_simulator', SIMULATOR_PATH))

except Exception as e:
# If we get any sort of error do not add the simulator
logging.debug(f'Failed to connect to Jade simulator at {SIMULATOR_PATH}')
logging.debug(e)
except Exception as e:
# If we get any sort of error do not add the simulator
logging.debug(f'Failed to connect to Jade simulator at {SIMULATOR_PATH}')
logging.debug(e)

return results
5 changes: 3 additions & 2 deletions hwilib/devices/keepkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,12 @@ def can_sign_taproot(self) -> bool:
return False


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
results = []
devs = hid.HidTransport.enumerate(usb_ids=KEEPKEY_HID_IDS)
devs.extend(webusb.WebUsbTransport.enumerate(usb_ids=KEEPKEY_WEBUSB_IDS))
devs.extend(udp.UdpTransport.enumerate(KEEPKEY_SIMULATOR_PATH))
if allow_emulators:
devs.extend(udp.UdpTransport.enumerate(KEEPKEY_SIMULATOR_PATH))
for dev in devs:
d_data: Dict[str, Any] = {}

Expand Down
5 changes: 3 additions & 2 deletions hwilib/devices/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,11 +546,12 @@ def can_sign_taproot(self) -> bool:
return isinstance(self.client, NewClient)


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
results = []
devices = []
devices.extend(hid.enumerate(LEDGER_VENDOR_ID, 0))
devices.append({'path': SIMULATOR_PATH.encode(), 'interface_number': 0, 'product_id': 0x1000})
if allow_emulators:
devices.append({'path': SIMULATOR_PATH.encode(), 'interface_number': 0, 'product_id': 0x1000})

for d in devices:
if ('interface_number' in d and d['interface_number'] == 0
Expand Down
5 changes: 3 additions & 2 deletions hwilib/devices/trezor.py
Original file line number Diff line number Diff line change
Expand Up @@ -851,11 +851,12 @@ def can_sign_taproot(self) -> bool:
return True


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> List[Dict[str, Any]]:
def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = True) -> List[Dict[str, Any]]:
results = []
devs = hid.HidTransport.enumerate()
devs.extend(webusb.WebUsbTransport.enumerate())
devs.extend(udp.UdpTransport.enumerate())
if allow_emulators:
devs.extend(udp.UdpTransport.enumerate())
for dev in devs:
d_data: Dict[str, Any] = {}

Expand Down
4 changes: 4 additions & 0 deletions test/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ def test_enumerate(self):
found = True
self.assertTrue(found)

def test_enumerate_no_emus(self):
enum_res = self.do_command(self.get_password_args() + ["enumerate", "--no-emulators"])
self.assertEqual(len(enum_res), 0)

def test_no_type(self):
gmxp_res = self.do_command(["--chain", "test", 'getmasterxpub', "--addr-type", "legacy"])
self.assertIn('error', gmxp_res)
Expand Down

0 comments on commit 217baa1

Please sign in to comment.