def get_session_info_trezor(get_public_node_fun, hw_session: HwSessionInfo, hw_client): nonlocal hw_type def call_get_public_node(ctrl, get_public_node_fun, path_n): pk = get_public_node_fun(path_n).node.public_key return pk path = fix_utils.get_default_bip32_base_path( hw_session.app_config.fix_network) path_n = fix_utils.bip32_path_string_to_n(path) # show message for Trezor T device while waiting for the user to choose the passphrase input method pub = WndUtils.run_thread_dialog(call_get_public_node, (get_public_node_fun, path_n), title=DEFAULT_HW_BUSY_TITLE, text=DEFAULT_HW_BUSY_MESSAGE, force_close_dlg_callback=partial( cancel_hw_thread_dialog, hw_client), show_window_delay_ms=1000) if pub: hw_session.set_base_info(path, pub) else: raise Exception('Couldn\'t read data from the hardware wallet.')
def hw_sign_message(hw_session: HwSessionInfo, bip32path, message, display_label: str = None): def sign(ctrl, display_label): ctrl.dlg_config_fun(dlg_title="Confirm message signing.", show_progress_bar=False) if not display_label: if hw_session.app_config.hw_type == HWType.ledger_nano_s: message_hash = hashlib.sha256(message.encode('ascii')).hexdigest().upper() display_label = '<b>Click the confirmation button on your hardware wallet to sign the message...</b>' \ '<br><br><b>Message:</b><br><span>' + message + '</span><br><br><b>SHA256 hash</b>:' \ '<br>' + message_hash else: display_label = '<b>Click the confirmation button on your hardware wallet to sign the message...</b>' ctrl.display_msg_fun(display_label) if hw_session.app_config.hw_type == HWType.trezor: import hw_intf_trezor as trezor return trezor.sign_message(hw_session, bip32path, message) elif hw_session.app_config.hw_type == HWType.keepkey: import hw_intf_keepkey as keepkey return keepkey.sign_message(hw_session, bip32path, message) elif hw_session.app_config.hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano as ledger return ledger.sign_message(hw_session, bip32path, message) else: logging.error('Invalid HW type: ' + str(hw_session.app_config.hw_type)) # execute the 'sign' function, but due to the fact that the call blocks the UI until the user clicks the HW # button, it's done inside a thread within a dialog that shows an appropriate message to the user sig = WndUtils.run_thread_dialog(sign, (display_label,), True) return sig
def hw_sign_message(hw_session: HwSessionInfo, bip32path, message, display_label: str = None): def sign(ctrl, display_label): ctrl.dlg_config_fun(dlg_title="Confirm message signing.", show_progress_bar=False) if display_label: ctrl.display_msg_fun(display_label) else: ctrl.display_msg_fun('<b>Click the confirmation button on your hardware wallet...</b>') if hw_session.app_config.hw_type == HWType.trezor: import hw_intf_trezor as trezor return trezor.sign_message(hw_session, bip32path, message) elif hw_session.app_config.hw_type == HWType.keepkey: import hw_intf_keepkey as keepkey return keepkey.sign_message(hw_session, bip32path, message) elif hw_session.app_config.hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano as ledger return ledger.sign_message(hw_session, bip32path, message) else: logging.error('Invalid HW type: ' + str(hw_session.app_config.hw_type)) # execute the 'sign' function, but due to the fact that the call blocks the UI until the user clicks the HW # button, it's done inside a thread within a dialog that shows an appropriate message to the user sig = WndUtils.run_thread_dialog(sign, (display_label,), True) return sig
def load_device_by_mnemonic(hw_type: HWType, hw_device_id: Optional[str], mnemonic_words: str, pin: str, passphrase_enbled: bool, hw_label: str, passphrase: str, secondary_pin: str, parent_window=None) -> Tuple[Optional[str], bool]: """ Initializes hardware wallet with a mnemonic words. For security reasons use this function only on an offline system, that will never be connected to the Internet. :param hw_type: app_config.HWType :param hw_device_id: id of the device selected by the user (TrezorClient, KeepkeyClient); None for Ledger Nano S :param mnemonic_words: string of 12/18/24 mnemonic words (separeted by spaces) :param pin: string with a new pin :param passphrase_enbled: if True, hw will have passphrase enabled (Trezor/Keepkey) :param hw_label: label for device (Trezor/Keepkey) :param passphrase: passphrase to be saved in the device (Ledger Nano S) :param secondary_pin: PIN securing passphrase (Ledger Nano S) :param parent_window: ref to a window according to which will be centered message dialogs created here :return: Tuple Ret[0]: Device id. If a device is wiped before initializing with mnemonics, a new device id is generated. It's returned to the caller. Ret[1]: True, if the user cancelled the operation. In this situation we deliberately don't raise the 'cancelled' exception, because in the case of changing of the device id (when wiping) we want to pass it back to the caller. Ret[0] and Ret[1] are None for Ledger devices. """ def load(ctrl, hw_device_id: str, mnemonic: str, pin: str, passphrase_enbled: bool, hw_label: str) -> \ Tuple[Optional[str], bool]: ctrl.dlg_config_fun(dlg_title="Please confirm", show_progress_bar=False) ctrl.display_msg_fun( '<b>Read the messages displyed on your hardware wallet <br>' 'and click the confirmation button when necessary...</b>') if hw_device_id: if hw_type == HWType.trezor: raise Exception('Feature no longer available for Trezor') elif hw_type == HWType.keepkey: from hw_intf_keepkey import load_device_by_mnemonic return load_device_by_mnemonic(hw_device_id, mnemonic, pin, passphrase_enbled, hw_label) else: raise Exception('Not supported by Ledger Nano S.') else: raise HWNotConnectedException() if hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano hw_intf_ledgernano.load_device_by_mnemonic(mnemonic_words, pin, passphrase, secondary_pin) return hw_device_id, False else: return WndUtils.run_thread_dialog( load, (hw_device_id, mnemonic_words, pin, passphrase_enbled, hw_label), True)
def hw_app_install(hw_session: HwSessionInfo, device: str, testnet: bool): def install(ctrl, device: str, testnet: bool) -> Tuple[int, str]: """ Returns the result code and respective message after attempting an app install. """ if device == "nanos": from hw_intf_ledgernano import Installer ledger = Installer(ctrl, testnet) name = "GINcoin" if not testnet else "GINcoin Testnet" tn = "" if not testnet else " testnet" ctrl.dlg_config_fun(dlg_title=f"Install {name}", show_progress_bar=False, min_width=250) ctrl.display_msg_fun(f"<b>Initialising...</b>") results = { 0x0000: f"{name} app installed successfully! REMINDER: This install method is not officially endorsed by Ledger. You will see a 'non genuine' confirmation prompt upon launch of the app. This is normal, and functionality is unchanged. This prompt will not appear when installed with the official Ledger Live Manager.", 0x0001: f"Error: {name} app installation failed.", 0x0002: f"Error: {name} app post-install verification failed! Please remove the app (with `uninstall nanos{tn}`) and then report this issue.", 0x0003: f"Error: Your version of the Bitcoin app is unknown. Please update both GINware and the Bitcoin app on your Ledger device, and try again. If the issue persists, please report this issue.", 0x0004: f"Error: App validation hashes not found!", 0x0005: f"Error: {name} app pre-install validation failed! Please report this issue.", 0x6484: f"Error: Are you sure you're using the latest firmware? Please update your Ledger device's firmware with the Ledger Live Manager.", 0x6985: f"Error: Cancelled by user.", 0x6a81: f"Error: The {name} app already seems to be installed. Please remove the {name} app first (with `uninstall nanos{tn}`).", 0x6a83: f"Error: Please install the Bitcoin app first, through the Manager tab in Ledger Live.", 0x6a84: f"Error: Insufficient space on device.", 0x6a85: f"Error: Insufficient space on device.", 0x6d00: f"Error: Please close any open app on your Ledger Nano S device and try again.", 0x6f00: f"Error: Please connect and unlock your Ledger Nano S device." } res = ledger.install() if res == 0: return (0, results.get(0)) elif results.get(res) == None: return (2, results.get(1) + " (code: " + hex(res) + ")") else: return (2, results.get(res)) return WndUtils.run_thread_dialog(install, (device, testnet), True, force_close_dlg_callback=partial( cancel_hw_thread_dialog, hw_session.hw_client))
def get_address(hw_session: HwSessionInfo, bip32_path: str, show_display: bool = False, message_to_display: str = None): def _get_address(ctrl, hw_session: HwSessionInfo, bip32_path: str, show_display: bool = False, message_to_display: str = None): if ctrl: ctrl.dlg_config_fun(dlg_title="Please confirm", show_progress_bar=False) if message_to_display: ctrl.display_msg_fun(message_to_display) else: ctrl.display_msg_fun('<b>Click the confirmation button on your hardware wallet to exit...</b>') client = hw_session.hw_client if client: if isinstance(bip32_path, str): bip32_path.strip() if bip32_path.lower().find('m/') >= 0: # removing m/ prefix because of keepkey library bip32_path = bip32_path[2:] if hw_session.app_config.hw_type == HWType.trezor: from trezorlib import btc if isinstance(bip32_path, str): bip32_path = dash_utils.bip32_path_string_to_n(bip32_path) return btc.get_address(client, hw_session.app_config.hw_coin_name, bip32_path, show_display) elif hw_session.app_config.hw_type == HWType.keepkey: if isinstance(bip32_path, str): bip32_path = dash_utils.bip32_path_string_to_n(bip32_path) return client.get_address(hw_session.app_config.hw_coin_name, bip32_path, show_display) elif hw_session.app_config.hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano as ledger if isinstance(bip32_path, list): # ledger requires bip32 path argument as a string bip32_path = bip32_path_n_to_string(bip32_path) adr_pubkey = ledger.get_address_and_pubkey(client, bip32_path) return adr_pubkey.get('address') else: raise Exception('Unknown hardware wallet type: ' + hw_session.app_config.hw_type) else: raise Exception('HW client not open.') if show_display: return WndUtils.run_thread_dialog(_get_address, (hw_session, bip32_path, show_display, message_to_display), True) else: return _get_address(None, hw_session, bip32_path, show_display, message_to_display)
def on_btnLocateCollateral_clicked(self, checked): break_scanning = False if not self.main_dlg.connect_hardware_wallet(): return def do_break_scanning(): nonlocal break_scanning break_scanning = True return False def check_break_scanning(): nonlocal break_scanning return break_scanning def apply_utxo(utxo): self.masternode.collateralAddress = utxo.address self.edtCollateralAddress.setText(utxo.address) self.masternode.collateralBip32Path = utxo.bip32_path self.edtCollateralPath.setText(utxo.bip32_path) self.masternode.collateralTx = utxo.txid self.edtCollateralTxHash.setText(utxo.txid) self.masternode.collateralTxIndex = str(utxo.output_index) self.edtCollateralTxIndex.setText(str(utxo.output_index)) self.update_ui_controls_state() self.set_modified() bip44_wallet = Bip44Wallet(self.app_config.hw_coin_name, self.main_dlg.hw_session, self.app_config.db_intf, self.crownd_intf, self.app_config.crown_network) utxos = WndUtils.run_thread_dialog( self.get_collateral_tx_address_thread, (bip44_wallet, check_break_scanning, self.edtCollateralAddress.text()), True, force_close_dlg_callback=do_break_scanning) if utxos: if len(utxos) == 1 and \ (not self.masternode.collateralAddress or (utxos[0].address_obj and self.masternode.collateralAddress == utxos[0].address_obj.address)) \ and (not self.masternode.collateralTx or utxos[0].txid == self.masternode.collateralTx): apply_utxo(utxos[0]) return dlg = ListCollateralTxsDlg(self, self.masternode, self.app_config, False, utxos) if dlg.exec_(): utxo = dlg.get_selected_utxo() if utxo: apply_utxo(utxo) else: if utxos is not None: WndUtils.warnMsg('Couldn\'t find any 10000 Crown UTXO in your wallet.')
def hw_encrypt_value(hw_session: HwSessionInfo, bip32_path_n: List[int], label: str, value: ByteString, ask_on_encrypt=True, ask_on_decrypt=True) -> Tuple[bytearray, bytearray]: """Encrypts a value with a hardware wallet. :param hw_session: :param bip32_path_n: bip32 path of the private key used for encryption :param label: key (in the meaning of key-value) used for encryption :param value: value being encrypted :param ask_on_encrypt: see Trezor doc :param ask_on_decrypt: see Trezor doc """ def encrypt(ctrl, hw_session: HwSessionInfo, bip32_path_n: List[int], label: str, value: bytearray): ctrl.dlg_config_fun(dlg_title="Data encryption", show_progress_bar=False) ctrl.display_msg_fun(f'<b>Encrypting \'{label}\'...</b>' f'<br><br>Enter the hardware wallet PIN/passphrase (if needed) to encrypt data.<br><br>' f'<b>Note:</b> encryption passphrase is independent from the wallet passphrase <br>' f'and can vary for each encrypted file.') if hw_session.hw_type == HWType.trezor: from trezorlib import misc, btc from trezorlib import exceptions try: client = hw_session.hw_client data = misc.encrypt_keyvalue(client, bip32_path_n, label, value, ask_on_encrypt, ask_on_decrypt) pub_key = btc.get_public_node(client, bip32_path_n).node.public_key return data, pub_key except (CancelException, exceptions.Cancelled): raise CancelException() elif hw_session.hw_type == HWType.keepkey: client = hw_session.hw_client data = client.encrypt_keyvalue(bip32_path_n, label, value, ask_on_encrypt, ask_on_decrypt) pub_key = client.get_public_node(bip32_path_n).node.public_key return data, pub_key elif hw_session.hw_type == HWType.ledger_nano_s: raise Exception('Feature not available for Ledger Nano S.') else: raise Exception('Invalid HW type: ' + str(hw_session)) if len(value) != 32: raise ValueError("Invalid password length (<> 32).") return WndUtils.run_thread_dialog(encrypt, (hw_session, bip32_path_n, label, value), True, force_close_dlg_callback=partial(cancel_hw_thread_dialog, hw_session.hw_client), show_window_delay_ms=200)
def hw_decrypt_value(hw_session: HwSessionInfo, bip32_path_n: List[int], label: str, value: ByteString, ask_on_encrypt=True, ask_on_decrypt=True) -> Tuple[bytearray, bytearray]: """ :param hw_session: :param passphrase_encoding: (for Keepkey only) it allows forcing the passphrase encoding compatible with BIP-39 standard (NFKD), which is used by Trezor devices; by default Keepkey uses non-standard encoding (NFC). :param bip32_path_n: bip32 path of the private key used for encryption :param label: key (in the meaning of key-value) used for encryption :param value: encrypted value to be decrypted, :param ask_on_encrypt: see Trezor doc :param ask_on_decrypt: see Trezor doc """ def decrypt(ctrl, hw_session: HwSessionInfo, bip32_path_n: List[int], label: str, value: bytearray): ctrl.dlg_config_fun(dlg_title="Data decryption", show_progress_bar=False) ctrl.display_msg_fun(f'<b>Decrypting \'{label}\'...</b><br><br>Enter the hardware wallet PIN/passphrase ' f'(if needed)<br> and click the confirmation button to decrypt data.') if hw_session.hw_type == HWType.trezor: from trezorlib import misc, btc from trezorlib import exceptions try: client = hw_session.hw_client data = misc.decrypt_keyvalue(client, bip32_path_n, label, value, ask_on_encrypt, ask_on_decrypt) pub_key = btc.get_public_node(client, bip32_path_n).node.public_key return data, pub_key except (CancelException, exceptions.Cancelled): raise CancelException() elif hw_session.hw_type == HWType.keepkey: client = hw_session.hw_client data = client.decrypt_keyvalue(bip32_path_n, label, value, ask_on_encrypt, ask_on_decrypt) pub_key = client.get_public_node(bip32_path_n).node.public_key return data, pub_key elif hw_session.hw_type == HWType.ledger_nano_s: raise Exception('Feature not available for Ledger Nano S.') else: raise Exception('Invalid HW type: ' + str(hw_session)) if len(value) != 32: raise ValueError("Invalid password length (<> 32).") return WndUtils.run_thread_dialog(decrypt, (hw_session, bip32_path_n, label, value), True, force_close_dlg_callback=partial(cancel_hw_thread_dialog, hw_session.hw_client))
def reset_device(hw_type: HWType, hw_device_id: str, word_count: int, passphrase_enabled: bool, pin_enabled: bool, hw_label: str, parent_window = None) -> Tuple[Optional[str], bool]: """ Initialize device with a newly generated words. :param hw_type: app_config.HWType :param hw_device_id: id of the device selected by the user (TrezorClient, KeepkeyClient); None for Ledger Nano S :param word_count: number of words (12/18/24) :param passphrase_enbled: if True, hw will have passphrase enabled (Trezor/Keepkey) :param pin_enabled: if True, hw will have pin enabled (Trezor/Keepkey) :param hw_label: label for device (Trezor/Keepkey) :param parent_window: ref to a window according to which will be centered message dialogs created here :return: Tuple Ret[0]: Device id. If a device is wiped before initializing with mnemonics, a new device id is generated. It's returned to the caller. Ret[1]: True, if the user cancelled the operation. In this situation we deliberately don't raise the 'cancelled' exception, because in the case of changing of the device id (when wiping) we want to pass it back to the caller function. Ret[0] and Ret[1] are None for Ledger devices. """ def load(ctrl, hw_type: HWType, hw_device_id: str, strength: int, passphrase_enabled: bool, pin_enabled: bool, hw_label: str) -> Tuple[Optional[str], bool]: ctrl.dlg_config_fun(dlg_title="Please confirm", show_progress_bar=False) ctrl.display_msg_fun('<b>Read the messages displyed on your hardware wallet <br>' 'and click the confirmation button when necessary...</b>') if hw_device_id: if hw_type == HWType.trezor: from hw_intf_trezor import reset_device return reset_device(hw_device_id, strength, passphrase_enabled, pin_enabled, hw_label) elif hw_type == HWType.keepkey: from hw_intf_keepkey import reset_device return reset_device(hw_device_id, strength, passphrase_enabled, pin_enabled, hw_label) else: raise Exception('Not supported by Ledger Nano S.') else: raise Exception('Not connected to a hardware wallet') if hw_type == HWType.ledger_nano_s: raise Exception('Not supported by Ledger Nano S.') else: if word_count not in (12, 18, 24): raise Exception('Invalid word count.') strength = {24: 32, 18: 24, 12: 16}.get(word_count) * 8 return WndUtils.run_thread_dialog(load, (hw_type, hw_device_id, strength, passphrase_enabled, pin_enabled, hw_label), True, center_by_window=parent_window)
def sign_tx(hw_session: HwSessionInfo, utxos_to_spend: List[UtxoType], tx_outputs: List[TxOutputType], tx_fee): """ Creates a signed transaction. :param main_ui: Main window for configuration data :param utxos_to_spend: list of utxos to send :param tx_outputs: destination addresses. Fields: 0: dest FIX address. 1: the output value in satoshis, 2: the bip32 path of the address if the output is the change address or None otherwise :param tx_fee: transaction fee :param rawtransactions: dict mapping txid to rawtransaction :return: tuple (serialized tx, total transaction amount in satoshis) """ def sign(ctrl): ctrl.dlg_config_fun(dlg_title="Confirm transaction signing.", show_progress_bar=False) ctrl.display_msg_fun( '<b>Click the confirmation button on your hardware wallet<br>' 'and wait for the transaction to be signed...</b>') if hw_session.app_config.hw_type == HWType.trezor: import hw_intf_trezor as trezor return trezor.sign_tx(hw_session, utxos_to_spend, tx_outputs, tx_fee) elif hw_session.app_config.hw_type == HWType.keepkey: import hw_intf_keepkey as keepkey return keepkey.sign_tx(hw_session, utxos_to_spend, tx_outputs, tx_fee) elif hw_session.app_config.hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano as ledger return ledger.sign_tx(hw_session, utxos_to_spend, tx_outputs, tx_fee) else: logging.error('Invalid HW type: ' + str(hw_session.app_config.hw_type)) # execute the 'prepare' function, but due to the fact that the call blocks the UI until the user clicks the HW # button, it's done inside a thread within a dialog that shows an appropriate message to the user sig = WndUtils.run_thread_dialog(sign, (), True, force_close_dlg_callback=partial( cancel_hw_thread_dialog, hw_session.hw_client)) return sig
def get_session_info_trezor(get_public_node_fun, hw_session: HwSessionInfo): nonlocal hw_type def call_get_public_node(ctrl, get_public_node_fun, path_n): pk = get_public_node_fun(path_n).node.public_key return pk path = dash_utils.get_default_bip32_base_path(hw_session.app_config.dash_network) path_n = dash_utils.bip32_path_string_to_n(path) # show message for Trezor T device while waiting for the user to choose the passphrase input method pub = WndUtils.run_thread_dialog(call_get_public_node, (get_public_node_fun, path_n), title='Confirm', text='<b>Complete the action on your hardware wallet device</b>', show_window_delay_ms=1000) hw_session.set_base_info(path, pub)
def on_btnLocateCollateral_clicked(self, checked): if not self.main_dlg.connect_hardware_wallet(): return def apply_utxo(utxo): self.masternode.collateralAddress = utxo.address self.edtCollateralAddress.setText(utxo.address) self.masternode.collateralBip32Path = utxo.bip32_path self.edtCollateralPath.setText(utxo.bip32_path) self.masternode.collateralTx = utxo.txid self.edtCollateralTxHash.setText(utxo.txid) self.masternode.collateralTxIndex = str(utxo.output_index) self.edtCollateralTxIndex.setText(str(utxo.output_index)) self.update_ui() self.set_modified() bip44_wallet = Bip44Wallet(self.app_config.hw_coin_name, self.main_dlg.hw_session, self.app_config.db_intf, self.dashd_intf, self.app_config.dash_network) utxos = WndUtils.run_thread_dialog( self.get_collateral_tx_address_thread, (bip44_wallet, ), True) if utxos: if len( utxos ) == 1 and not self.masternode.collateralAddress and not self.masternode.collateralTx: used = False for mn in self.app_config.masternodes: if utxos[0].address == mn.collateralAddress or mn.collateralTx + '-' + str(mn.collateralTxIndex) == \ utxos[0].txid + '-' + str(utxos[0].output_index): used = True break if not used: apply_utxo(utxos[0]) return dlg = ListCollateralTxsDlg(self, self.masternode, self.app_config, False, utxos) if dlg.exec_(): utxo = dlg.get_selected_utxo() if utxo: apply_utxo(utxo) else: if utxos is not None: WndUtils.warnMsg( 'Couldn\'t find any 1000 Dash UTXO in your wallet.')
def wipe_device(hw_type: HWType, hw_device_id: Optional[str], parent_window=None) -> Tuple[Optional[str], bool]: """ Wipes the hardware wallet device. :param hw_type: app_config.HWType :param hw_device_id: id of the device selected by the user (TrezorClient, KeepkeyClient) :param parent_window: ref to a window according to which will be centered message dialogs created here :return: Tuple Ret[0]: Device id. After wiping a new device id is generated, which is returned to the caller. Ret[1]: True, if the user cancelled the operation. In this situation we deliberately don't raise the 'cancelled' exception, because in the case of changing of the device id (when wiping) we want to pass it back to the caller. """ def wipe(ctrl): ctrl.dlg_config_fun(dlg_title="Confirm wiping device.", show_progress_bar=False) ctrl.display_msg_fun( '<b>Read the messages displyed on your hardware wallet <br>' 'and click the confirmation button when necessary...</b>') if hw_type == HWType.trezor: from hw_intf_trezor import wipe_device return wipe_device(hw_device_id) elif hw_type == HWType.keepkey: from hw_intf_keepkey import wipe_device return wipe_device(hw_device_id) elif hw_type == HWType.ledger_nano_s: raise Exception('Not supported by Ledger Nano S.') else: raise Exception('Invalid HW type: ' + str(hw_type)) # execute the 'wipe' inside a thread to avoid blocking UI return WndUtils.run_thread_dialog(wipe, (), True, center_by_window=parent_window)
def hw_app_uninstall(hw_session: HwSessionInfo, device: str, testnet: bool): def uninstall(ctrl, device: str, testnet: bool) -> Tuple[int, str]: """ Returns the result code and respective message after attempting an app removal. """ if device == "nanos": from hw_intf_ledgernano import Installer ledger = Installer(ctrl, testnet) name = "GINcoin" if not testnet else "GINcoin Testnet" ctrl.dlg_config_fun(dlg_title=f"Uninstall {name}", show_progress_bar=False, min_width=250) ctrl.display_msg_fun(f"<b>Initialising...</b>") results = { 0x0000: f"{name} app removed.", 0x0001: f"Error: {name} app removal failed.", 0x6484: f"Error: Are you sure you're using the latest firmware? Please update your Ledger device's firmware with the Ledger Live Manager.", 0x6985: f"Error: Cancelled by user.", 0x6d00: f"Error: Please close any open app on your Ledger Nano S device and try again.", 0x6f00: f"Error: Please connect and unlock your Ledger Nano S device." } res = ledger.uninstall() if res == 0: return (0, results.get(0)) elif results.get(res) == None: return (2, results.get(1) + " (code: " + hex(res) + ")") else: return (2, results.get(res)) return WndUtils.run_thread_dialog(uninstall, (device, testnet), True, force_close_dlg_callback=partial( cancel_hw_thread_dialog, hw_session.hw_client))
def verify_data(self): self.dmn_collateral_tx = self.edtCollateralTx.text().strip() try: self.dmn_collateral_tx_index = int(self.edtCollateralIndex.text()) if self.dmn_collateral_tx_index < 0: raise Exception('Invalid transaction index') except Exception: self.edtCollateralIndex.setFocus() raise Exception( 'Invalid collateral transaction index: should be integer greater or equal 0.' ) try: self.dmn_ip = self.edtIP.text().strip() if self.dmn_ip: ipaddress.ip_address(self.dmn_ip) except Exception as e: self.edtIP.setFocus() raise Exception('Invalid masternode IP address: %s.' % str(e)) try: if self.dmn_ip: self.dmn_tcp_port = int(self.edtPort.text()) else: self.dmn_tcp_port = None except Exception: self.edtPort.setFocus() raise Exception('Invalid TCP port: should be integer.') self.dmn_owner_payout_addr = self.edtPayoutAddress.text().strip() if not validate_address(self.dmn_owner_payout_addr, self.app_config.dash_network): self.edtPayoutAddress.setFocus() raise Exception('Invalid owner payout address.') if self.chbWholeMNReward.isChecked(): self.dmn_operator_reward = 0 else: self.dmn_operator_reward = self.edtOperatorReward.value() if self.dmn_operator_reward > 100 or self.dmn_operator_reward < 0: self.edtOperatorReward.setFocus() raise Exception( 'Invalid operator reward value: should be a value between 0 and 100.' ) self.dmn_owner_privkey = self.edtOwnerKey.text().strip() if not validate_wif_privkey(self.dmn_owner_privkey, self.app_config.dash_network): self.edtOwnerKey.setFocus() self.upd_owner_key_info(True) raise Exception('Invalid owner private key.') else: self.dmn_owner_address = wif_privkey_to_address( self.dmn_owner_privkey, self.app_config.dash_network) try: self.dmn_operator_privkey = self.edtOperatorKey.text().strip() self.dmn_operator_pubkey = bls_privkey_to_pubkey( self.dmn_operator_privkey) except Exception as e: self.upd_operator_key_info(True) self.edtOperatorKey.setFocus() raise Exception('Invalid operator private key: ' + str(e)) self.dmn_voting_privkey = self.edtVotingKey.text().strip() if not validate_wif_privkey(self.dmn_voting_privkey, self.app_config.dash_network): self.upd_voting_key_info(True) self.edtVotingKey.setFocus() raise Exception('Invalid voting private key.') else: self.dmn_voting_address = wif_privkey_to_address( self.dmn_voting_privkey, self.app_config.dash_network) self.btnContinue.setEnabled(False) self.btnContinue.repaint() ret = WndUtils.run_thread_dialog(self.get_collateral_tx_address_thread, (), True) self.btnContinue.setEnabled(True) self.btnContinue.repaint() return ret
def recover_device_with_seed_input(dongle: HIDDongleHIDAPI, mnemonic_words: str, pin: str, passphrase: str, secondary_pin: str): """ Initialize Ledger Nano S device with a list of mnemonic words. :param mnemonic_words: 12, 18 or 24 mnemonic words separated with spaces to initialise device. :param pin: PIN to be set in the device (4- or 8-character string) :param passphrase: Passphrase to be set in the device or empty. :param secondary_pin: Secondary PIN to activate passph. It's required if 'passph' is set. """ def process(ctrl): ctrl.dlg_config(dlg_title="Please confirm", show_progress_bar=False) ctrl.display_msg('<b>Please wait while initializing device...</b>') # stage 1: initialize the hardware wallet with mnemonic words apdudata = bytearray() if pin: apdudata += bytearray([len(pin)]) + bytearray(pin, 'utf8') else: apdudata += bytearray([0]) # empty prefix apdudata += bytearray([0]) # empty passph in this phase apdudata += bytearray([0]) if mnemonic_words: apdudata += bytearray([len(mnemonic_words)]) + bytearray( mnemonic_words, 'utf8') else: apdudata += bytearray([0]) apdu = bytearray([0xE0, 0xD0, 0x00, 0x00, len(apdudata)]) + apdudata dongle.exchange(apdu, timeout=3000) # stage 2: setup the secondary pin and the passph if provided if passphrase and secondary_pin: ctrl.display_msg( '<b>Configuring passphrase, enter the primary PIN on your <br>' 'hardware wallet when asked...</b>') apdudata = bytearray() if pin: apdudata += bytearray([len(pin)]) + bytearray( secondary_pin, 'utf8') else: apdudata += bytearray([0]) # empty prefix apdudata += bytearray([0]) if passphrase: _passph = unicodedata.normalize('NFKD', passphrase) apdudata += bytearray([len(_passph)]) + bytearray( _passph, 'utf8') else: apdudata += bytearray([0]) # empty mnemonic words in this phase apdudata += bytearray([0]) apdu = bytearray([0xE0, 0xD0, 0x01, 0x00, len(apdudata)]) + apdudata dongle.exchange(apdu, timeout=3000) device_id = dongle.__getattribute__('hidDevicePath') return device_id was_open = is_dongle_open(dongle) try: open_dongle(dongle) return WndUtils.run_thread_dialog(process, (), True) except BTChipException as e: if e.message in ('Invalid status 6982', 'Invalid status 6d00'): raise Exception( 'Operation failed with the following error: %s. \n\nMake sure you have reset the device ' 'and started it in recovery mode.' % e.message) else: raise finally: if not was_open and is_dongle_open(dongle): dongle.close()
def load_device_by_mnemonic(mnemonic_words: str, pin: str, passphrase: str, secondary_pin: str): """ Initialise Ledger Nano S device with a list of mnemonic words. :param mnemonic_words: 12, 18 or 24 mnemonic words separated with spaces to initialise device. :param pin: PIN to be set in the device (4- or 8-character string) :param passphrase: Passphrase to be set in the device or empty. :param secondary_pin: Secondary PIN to activate passphrase. It's required if 'passphrase' is set. """ def process(ctrl, mnemonic_words, pin, passphrase, secondary_pin): ctrl.dlg_config_fun(dlg_title="Please confirm", show_progress_bar=False) ctrl.display_msg_fun('<b>Please wait while initializing device...</b>') dongle = getDongle() # stage 1: initialize the hardware wallet with mnemonic words apdudata = bytearray() if pin: apdudata += bytearray([len(pin)]) + bytearray(pin, 'utf8') else: apdudata += bytearray([0]) # empty prefix apdudata += bytearray([0]) # empty passphrase in this phase apdudata += bytearray([0]) if mnemonic_words: apdudata += bytearray([len(mnemonic_words)]) + bytearray( mnemonic_words, 'utf8') else: apdudata += bytearray([0]) apdu = bytearray([0xE0, 0xD0, 0x00, 0x00, len(apdudata)]) + apdudata dongle.exchange(apdu, timeout=3000) # stage 2: setup the secondary pin and the passphrase if provided if passphrase and secondary_pin: ctrl.display_msg_fun( '<b>Configuring the passphrase, enter the primary PIN on your <br>' 'hardware wallet when asked...</b>') apdudata = bytearray() if pin: apdudata += bytearray([len(pin)]) + bytearray( secondary_pin, 'utf8') else: apdudata += bytearray([0]) # empty prefix apdudata += bytearray([0]) if passphrase: passphrase = unicodedata.normalize('NFKD', passphrase) apdudata += bytearray([len(passphrase)]) + bytearray( passphrase, 'utf8') else: apdudata += bytearray([0]) # empty mnemonic words in this phase apdudata += bytearray([0]) apdu = bytearray([0xE0, 0xD0, 0x01, 0x00, len(apdudata)]) + apdudata dongle.exchange(apdu, timeout=3000) dongle.close() del dongle try: return WndUtils.run_thread_dialog( process, (mnemonic_words, pin, passphrase, secondary_pin), True) except BTChipException as e: if e.message == 'Invalid status 6982': raise Exception( 'Operation failed with the following error: %s. \n\nMake sure you have reset the device ' 'and started it in recovery mode.' % e.message) else: raise except Exception as e: raise
def get_address(hw_session: HwSessionInfo, bip32_path: str, show_display: bool = False, message_to_display: str = None): def _get_address(ctrl, hw_session: HwSessionInfo, bip32_path: str, show_display: bool = False, message_to_display: str = None): if ctrl: ctrl.dlg_config_fun(dlg_title=DEFAULT_HW_BUSY_TITLE, show_progress_bar=False) if message_to_display: ctrl.display_msg_fun(message_to_display) else: ctrl.display_msg_fun( '<b>Click the confirmation button on your hardware wallet to exit...</b>' ) client = hw_session.hw_client if client: if isinstance(bip32_path, str): bip32_path.strip() if bip32_path.lower().find('m/') >= 0: # removing m/ prefix because of keepkey library bip32_path = bip32_path[2:] if hw_session.app_config.hw_type == HWType.trezor: from trezorlib import btc from trezorlib import exceptions try: if isinstance(bip32_path, str): bip32_path = fix_utils.bip32_path_string_to_n( bip32_path) ret = btc.get_address(client, hw_session.app_config.hw_coin_name, bip32_path, show_display) return ret except (CancelException, exceptions.Cancelled): raise CancelException() elif hw_session.app_config.hw_type == HWType.keepkey: from keepkeylib.client import CallException try: if isinstance(bip32_path, str): bip32_path = fix_utils.bip32_path_string_to_n( bip32_path) return client.get_address( hw_session.app_config.hw_coin_name, bip32_path, show_display) except CallException as e: if isinstance(e.args, tuple) and len(e.args) >= 2 and isinstance(e.args[1], str) and \ e.args[1].find('cancel') >= 0: raise CancelException('Cancelled') elif hw_session.app_config.hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano as ledger if isinstance(bip32_path, list): # ledger requires bip32 path argument as a string bip32_path = bip32_path_n_to_string(bip32_path) adr_pubkey = ledger.get_address_and_pubkey( client, bip32_path, show_display) return adr_pubkey.get('address') else: raise Exception('Unknown hardware wallet type: ' + hw_session.app_config.hw_type) else: raise Exception('HW client not open.') if message_to_display or show_display: msg_delay = 0 else: msg_delay = 1000 message_to_display = DEFAULT_HW_BUSY_MESSAGE return WndUtils.run_thread_dialog( _get_address, (hw_session, bip32_path, show_display, message_to_display), True, show_window_delay_ms=msg_delay, force_close_dlg_callback=partial(cancel_hw_thread_dialog, hw_session.hw_client))