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 = gewel_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 = gewel_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.')
def sign_message(hw_session: HwSessionInfo, bip32path, message): client = hw_session.hw_client address_n = dash_utils.bip32_path_string_to_n(bip32path) try: return btc.sign_message(client, hw_session.app_config.hw_coin_name, address_n, message) except exceptions.Cancelled: raise CancelException('Cancelled')
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))
def callback_PassphraseRequest(self, msg): passphrase = self.ask_for_pass_fun() if passphrase is None: raise CancelException('Cancelled') else: if self.passphrase_encoding in ('NFKD', 'NFC'): passphrase = unicodedata.normalize(self.passphrase_encoding, passphrase) else: raise Exception('Invalid passphrase encoding value: ' + self.passphrase_encoding) return keepkey_proto.PassphraseAck(passphrase=passphrase)
def sign_message(hw_session: HwSessionInfo, bip32path, message): client = hw_session.hw_client address_n = client.expand_path(clean_bip32_path(bip32path)) try: return client.sign_message(hw_session.app_config.hw_coin_name, address_n, message) except CallException as e: if e.args and len(e.args) >= 2 and e.args[1].lower().find('cancelled') >= 0: raise CancelException('Cancelled') else: raise
def sign_message(hw_client, hw_coin_name: str, bip32path: str, message: str): address_n = hw_client.expand_path(clean_bip32_path(bip32path)) try: return hw_client.sign_message(hw_coin_name, address_n, message) except CallException as e: if e.args and len( e.args) >= 2 and e.args[1].lower().find('cancelled') >= 0: raise CancelException('Cancelled') else: raise
def sign_message(hw_client, hw_coin_name: str, bip32path: str, message: str): address_n = dash_utils.bip32_path_string_to_n(bip32path) try: return btc.sign_message(hw_client, hw_coin_name, address_n, message) except exceptions.Cancelled: raise CancelException('Cancelled') except exceptions.TrezorFailure as e: if e.failure.message == 'Device not initialized': raise HwNotInitialized(e.failure.message) else: raise
def validate_firmware_internal(self, version, fw, expected_fingerprint=None): """Adapted version of the 'validate_firmware' function from trezorlib""" if version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE: if fw.embedded_onev2: log.debug( "Trezor One firmware with embedded v2 image (1.8.0 or later)" ) else: log.debug("Trezor One firmware image.") elif version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE_V2: log.debug("Trezor One v2 firmware (1.8.0 or later)") elif version == trezorlib.firmware.FirmwareFormat.TREZOR_T: log.debug("Trezor T firmware image.") vendor = fw.vendor_header.text vendor_version = "{major}.{minor}".format( **fw.vendor_header.version) log.debug("Vendor header from {}, version {}".format( vendor, vendor_version)) try: trezorlib.firmware.validate(version, fw, allow_unsigned=False) log.debug("Signatures are valid.") except trezorlib.firmware.Unsigned: if WndUtils.query_dlg( 'No signatures found. Continue?', buttons=QMessageBox.Yes | QMessageBox.No, default_button=QMessageBox.Yes, icon=QMessageBox.Information) == QMessageBox.No: raise CancelException() try: trezorlib.firmware.validate(version, fw, allow_unsigned=True) log.debug("Unsigned firmware looking OK.") except trezorlib.firmware.FirmwareIntegrityError as e: log.exception(e) raise Exception("Firmware validation failed, aborting.") except trezorlib.firmware.FirmwareIntegrityError as e: log.exception(e) raise Exception("Firmware validation failed, aborting.") fingerprint = trezorlib.firmware.digest(version, fw).hex() log.debug("Firmware fingerprint: {}".format(fingerprint)) if version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE and fw.embedded_onev2: fingerprint_onev2 = trezorlib.firmware.digest( trezorlib.firmware.FirmwareFormat.TREZOR_ONE_V2, fw.embedded_onev2).hex() log.debug( "Embedded v2 image fingerprint: {}".format(fingerprint_onev2)) if expected_fingerprint and fingerprint != expected_fingerprint: log.error("Expected fingerprint: {}".format(expected_fingerprint)) raise Exception("Fingerprints do not match, aborting.")
def callback_PinMatrixRequest(self, msg): if msg.type == 1: desc = 'Enter current PIN' elif msg.type == 2: desc = 'Enter new PIN' elif msg.type == 3: desc = 'Enter new PIN again' else: desc = 'Enter PIN' pin = self.ask_for_pin_fun(desc) if not pin: raise CancelException('Cancelled') return keepkey_proto.PinMatrixAck(pin=pin)
def change_pin(hw_client, remove=False): if hw_client: try: device.change_pin(hw_client, remove) except exceptions.Cancelled: raise CancelException('Cancelled') except exceptions.TrezorFailure as e: if e.failure.message == 'Device not initialized': raise HwNotInitialized(e.failure.message) else: raise else: raise Exception('HW client not set.')
def cancel_hw_thread_dialog(hw_client): try: hw_type = get_hw_type(hw_client) if hw_type == HWType.trezor: hw_client.cancel() elif hw_type == HWType.keepkey: hw_client.cancel() elif hw_type == HWType.ledger_nano_s: return False raise CancelException('Cancel') except CancelException: raise except Exception as e: logging.warning('Error when canceling hw session. Details: %s', str(e)) return True
def firmware_update(self, fingerprint: str, firmware_data: bytes): """Adapted version of the 'firmware_update' function from trezorlib""" f = self.features bootloader_version = (f.major_version, f.minor_version, f.patch_version) bootloader_onev2 = f.major_version == 1 and bootloader_version >= ( 1, 8, 0) model = f.model or "1" try: version, fw = trezorlib.firmware.parse(firmware_data) except Exception as e: raise self.validate_firmware_internal(version, fw, fingerprint) if (bootloader_onev2 and version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE and not fw.embedded_onev2): raise Exception("Firmware is too old for your device. Aborting.") elif not bootloader_onev2 and version == trezorlib.firmware.FirmwareFormat.TREZOR_ONE_V2: raise Exception("You need to upgrade to bootloader 1.8.0 first.") if f.major_version not in ALLOWED_FIRMWARE_FORMATS: raise Exception("DMT doesn't know your device version. Aborting.") elif version not in ALLOWED_FIRMWARE_FORMATS[f.major_version]: raise Exception("Firmware does not match your device, aborting.") if bootloader_onev2 and firmware_data[:4] == b"TRZR" and firmware_data[ 256:256 + 4] == b"TRZF": log.debug("Extracting embedded firmware image.") firmware_data = firmware_data[256:] try: # if f.major_version == 1 and f.firmware_present is not False: # # Trezor One does not send ButtonRequest # "Please confirm the action on your Trezor device" trezorlib.firmware.update(self, firmware_data) return True except exceptions.Cancelled: raise CancelException("Update aborted on device.") except exceptions.TrezorException as e: raise Exception("Update failed: {}".format(e))
def read_file_encrypted( file_name: str, ret_attrs: dict, hw_session: HwSessionInfo) -> Generator[bytes, None, None]: ret_attrs['encrypted'] = False try: hw_session.save_state() with open(file_name, 'rb') as f_ptr: data = f_ptr.read(len(DMT_ENCRYPTED_DATA_PREFIX)) if data == DMT_ENCRYPTED_DATA_PREFIX: ret_attrs['encrypted'] = True protocol = read_varint_from_file(f_ptr) if protocol == 1: # with Trezor method + Fernet hw_type_bin = read_varint_from_file(f_ptr) hw_type = { 1: HWType.trezor, 2: HWType.keepkey, 3: HWType.ledger_nano }.get(hw_type_bin) if hw_type: # connect hardware wallet, choosing the type compatible with the type read from # the encrypted file if hw_session.hw_client: if (hw_type in (HWType.trezor, HWType.keepkey) and hw_session.hw_type not in (HWType.trezor, HWType.keepkey)) or \ (hw_type == HWType.ledger_nano and hw_type != hw_session.hw_type): # if the currently connected hardware wallet type is not compatible with the # type from the encrypted file, disconnect it to give a user a chance to choose # the correct one in the code below hw_session.disconnect_hardware_wallet() if not hw_session.hw_client: if hw_type in (HWType.trezor, HWType.keepkey): hw_session.set_hw_types_allowed( (HWType.trezor, HWType.keepkey)) else: hw_session.set_hw_types_allowed((hw_type, )) if not hw_session.connect_hardware_wallet(): raise HWNotConnectedException( f'This file was encrypted with {HWType.get_desc(hw_type)} hardware wallet, ' f'which has to be connected to the computer decrypt the file.' ) data_label_bin = read_bytes_from_file(f_ptr) label = base64.urlsafe_b64decode( data_label_bin).decode('utf-8') encrypted_key_bin = read_bytes_from_file(f_ptr) bip32_path_n = read_int_list_from_file(f_ptr) pub_key_hash_hdr = read_bytes_from_file(f_ptr) while True: if not hw_session.hw_client: raise HWNotConnectedException( f'This file was encrypted with {HWType.get_desc(hw_type)} hardware wallet, ' f'which has to be connected to the computer decrypt the file.' ) if hw_session.hw_type in (HWType.trezor, HWType.keepkey): key_bin, pub_key = hw_session.hw_decrypt_value( bip32_path_n, label=label, value=encrypted_key_bin) elif hw_session.hw_type == HWType.ledger_nano: display_label = f'<b>Click the sign message confirmation button on the <br>' \ f'hardware wallet to decrypt \'{label}\'.</b>' bip32_path_str = bip32_path_n_to_string( bip32_path_n) sig = hw_sign_message( hw_session, 'Dash', bip32_path_str, encrypted_key_bin.hex(), display_label=display_label) adr_pk = get_address_and_pubkey( hw_session, 'Dash', bip32_path_str) pub_key = adr_pk.get('publicKey') key_bin = SHA256.new(sig.signature).digest() else: raise Exception( 'Invalid hardware wallet type.') pub_key_hash = SHA256.new(pub_key).digest() if pub_key_hash_hdr == pub_key_hash: break url = get_note_url('DMT0003') if WndUtils.query_dlg( message= 'Inconsistency between encryption and decryption keys.\n\n' 'The reason may be using a different passphrase than it was used ' 'for encryption or running another application communicating with the ' 'device simultaneously, like Trezor web wallet (see <a href="{url}">' 'here</a>).\n\n' 'Do you want to try again?', buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning ) == QMessageBox.Cancel: raise CancelException('User cancelled.') hw_session.disconnect_hardware_wallet() hw_session.connect_hardware_wallet() key = base64.urlsafe_b64encode(key_bin) fer = Fernet(key) while True: # data is written in blocks; if front of each block there is a block size value data_bin = f_ptr.read(8) if len(data_bin) == 0: break # end of file elif len(data_bin) < 8: raise ValueError( 'File end before read completed.') data_chunk_size = int.from_bytes( data_bin, byteorder='little') if data_chunk_size < 0 or data_chunk_size > 2000000000: raise ValueError( 'Data corrupted: invalid data chunk size.') data_bin = f_ptr.read(data_chunk_size) if data_chunk_size != len(data_bin): raise ValueError( 'File end before read completed.') data_base64 = base64.urlsafe_b64encode(data_bin) try: data_decr = fer.decrypt(data_base64) except InvalidToken: raise Exception( 'Couldn\'t decrypt file (InvalidToken error). The file is probably ' 'corrupted or is encrypted with a different encryption method.' ) yield data_decr else: raise ValueError('Invalid hardware wallet type value.') else: raise ValueError('Invalid protocol value.') else: # the data inside the file isn't encrypted # read and yield raw data while True: # data is written in blocks; if front of each block there is a block size value data += f_ptr.read(ENC_FILE_BLOCK_SIZE) if not len(data): break yield data data = bytes() finally: hw_session.restore_state()
def _callback_pin(self, msg): try: return TrezorClient._callback_pin(self, msg) except exceptions.Cancelled: raise CancelException('Cancelled')
def sign_tx(hw_session: HwSessionInfo, utxos_to_spend: List[wallet_common.UtxoType], tx_outputs: List[wallet_common.TxOutputType], tx_fee): """ Creates a signed transaction. :param hw_session: :param utxos_to_spend: list of utxos to send :param tx_outputs: list of transaction outputs :param tx_fee: transaction fee :return: tuple (serialized tx, total transaction amount in satoshis) """ def load_prev_txes(tx_api, skip_cache: bool = False): txes = {} tx_api.skip_cache = skip_cache for utxo in utxos_to_spend: prev_hash = bytes.fromhex(utxo.txid) if prev_hash not in txes: tx = tx_api[prev_hash] txes[prev_hash] = tx return txes insight_network = 'insight_polis' if hw_session.app_config.is_testnet(): insight_network += '_testnet' polis_network = hw_session.app_config.polis_network c_name = hw_session.app_config.hw_coin_name coin = coins.by_name[c_name] url = hw_session.app_config.get_tx_api_url() coin['bitcore'].clear() coin['bitcore'].append(url) tx_api = MyTxApiInsight(coin, '', hw_session.polisd_intf, hw_session.app_config.tx_cache_dir) client = hw_session.hw_client inputs = [] outputs = [] inputs_amount = 0 for utxo_index, utxo in enumerate(utxos_to_spend): if not utxo.bip32_path: raise Exception('No BIP32 path for UTXO ' + utxo.txid) address_n = polis_utils.bip32_path_string_to_n(utxo.bip32_path) it = trezor_proto.TxInputType(address_n=address_n, prev_hash=binascii.unhexlify(utxo.txid), prev_index=int(utxo.output_index)) inputs.append(it) inputs_amount += utxo.satoshis outputs_amount = 0 for out in tx_outputs: outputs_amount += out.satoshis if out.address[0] in polis_utils.get_chain_params( polis_network).B58_PREFIXES_SCRIPT_ADDRESS: stype = trezor_proto.OutputScriptType.PAYTOSCRIPTHASH logging.debug('Transaction type: PAYTOSCRIPTHASH' + str(stype)) elif out.address[0] in polis_utils.get_chain_params( polis_network).B58_PREFIXES_PUBKEY_ADDRESS: stype = trezor_proto.OutputScriptType.PAYTOADDRESS logging.debug('Transaction type: PAYTOADDRESS ' + str(stype)) else: raise Exception('Invalid prefix of the destination address.') if out.bip32_path: address_n = polis_utils.bip32_path_string_to_n(out.bip32_path) else: address_n = None ot = trezor_proto.TxOutputType( address=out.address if address_n is None else None, address_n=address_n, amount=out.satoshis, script_type=stype) outputs.append(ot) if outputs_amount + tx_fee != inputs_amount: raise Exception( 'Transaction validation failure: inputs + fee != outputs') try: for skip_cache in (False, True): txes = load_prev_txes(tx_api, skip_cache) try: signed = btc.sign_tx(client, hw_session.app_config.hw_coin_name, inputs, outputs, prev_txes=txes) return signed[1], inputs_amount except exceptions.Cancelled: raise except Exception as e: if skip_cache: raise log.exception( 'Exception occurred while signing transaction. Turning off the transaction cache ' 'and retrying...') raise Exception('Internal error: transaction not signed') except exceptions.Cancelled: raise CancelException('Cancelled')
def connect_hw(hw_session: Optional[HwSessionInfo], hw_type: HWType, device_id: Optional[str] = 'NFC', passphrase_encoding: Optional[str] = None): """ Initializes connection with a hardware wallet. :param hw_type: symbol of the hardware wallet type :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). :return: """ 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.') control_trezor_keepkey_libs(hw_type) if hw_type == HWType.trezor: import hw_intf_trezor as trezor import trezorlib.client as client from trezorlib import btc, exceptions try: if hw_session and hw_session.app_config: use_webusb = hw_session.app_config.trezor_webusb use_bridge = hw_session.app_config.trezor_bridge use_udp = hw_session.app_config.trezor_udp use_hid = hw_session.app_config.trezor_hid else: use_webusb = True use_bridge = True use_udp = True use_hid = True cli = trezor.connect_trezor(device_id=device_id, use_webusb=use_webusb, use_bridge=use_bridge, use_udp=use_udp, use_hid=use_hid) if cli and hw_session: try: get_public_node_fun = partial(btc.get_public_node, cli) get_session_info_trezor(get_public_node_fun, hw_session, cli) except (CancelException, exceptions.Cancelled): # cancel_hw_operation(cli) disconnect_hw(cli) raise CancelException() except Exception as e: # in the case of error close the session disconnect_hw(cli) raise return cli except exceptions.PinException as e: raise HardwareWalletPinException(e.args[1]) elif hw_type == HWType.keepkey: import hw_intf_keepkey as keepkey import keepkeylib.client as client try: cli = keepkey.connect_keepkey( passphrase_encoding=passphrase_encoding, device_id=device_id) if cli and hw_session: try: get_session_info_trezor(cli.get_public_node, hw_session, cli) except CancelException: cancel_hw_operation(cli) disconnect_hw(cli) raise except Exception: # in the case of error close the session disconnect_hw(cli) raise return cli except client.PinException as e: raise HardwareWalletPinException(e.args[1]) elif hw_type == HWType.ledger_nano_s: import hw_intf_ledgernano as ledger cli = ledger.connect_ledgernano() if cli and hw_session: try: path = fix_utils.get_default_bip32_base_path( hw_session.app_config.fix_network) ap = ledger.get_address_and_pubkey(cli, path) hw_session.set_base_info(path, ap['publicKey']) except CancelException: cancel_hw_operation(cli) disconnect_hw(cli) raise except Exception: # in the case of error close the session disconnect_hw(cli) raise return cli else: raise Exception('Invalid HW type: ' + str(hw_type))
def callback_WordRequest(self, msg): msg = "Enter one word of mnemonic: " word = ask_for_word_callback(msg, self.__mnemonic.wordlist) if not word: raise CancelException('Cancelled') return keepkey_proto.WordAck(word=word)
def sign_message(hw_session: HwSessionInfo, bip32_path, message): client = hw_session.hw_client # Ledger doesn't accept characters other that ascii printable: # https://ledgerhq.github.io/btchip-doc/bitcoin-technical.html#_sign_message message = message.encode('ascii', 'ignore') bip32_path = clean_bip32_path(bip32_path) ok = False for i in range(1, 4): info = client.signMessagePrepare(bip32_path, message) if info['confirmationNeeded'] and info['confirmationType'] == 34: if i == 1 or \ WndUtils.queryDlg('Another application (such as Ledger Wallet Bitcoin app) has probably taken over ' 'the communication with the Ledger device.' '\n\nTo continue, close that application and click the <b>Retry</b> button.' '\nTo cancel, click the <b>Abort</b> button', buttons=QMessageBox.Retry | QMessageBox.Abort, default_button=QMessageBox.Retry, icon=QMessageBox.Warning) == QMessageBox.Retry: # we need to reconnect the device; first, we'll try to reconnect to HW without closing the intefering # application; it it doesn't help we'll display a message requesting the user to close the app hw_session.hw_disconnect() if hw_session.hw_connect(): client = hw_session.hw_client else: raise Exception('Hardware wallet reconnect error.') else: break else: ok = True break if not ok: raise CancelException('Cancelled') try: signature = client.signMessageSign() except Exception as e: logging.exception('Exception while signing message with Ledger Nano S') raise Exception( 'Exception while signing message with Ledger Nano S. Details: ' + str(e)) try: pubkey = client.getWalletPublicKey(bip32_path) except Exception as e: logging.exception( 'Could not get public key for BIP32 path from Ledger Nano S') raise Exception( 'Could not get public key for BIP32 path from Ledger Nano S. Details: ' + str(e)) if len(signature) > 4: r_length = signature[3] r = signature[4:4 + r_length] if len(signature) > 4 + r_length + 1: s_length = signature[4 + r_length + 1] if len(signature) > 4 + r_length + 2: s = signature[4 + r_length + 2:] if r_length == 33: r = r[1:] if s_length == 33: s = s[1:] else: logging.error( 'client.signMessageSign() returned invalid response (code 3): ' + signature.hex()) raise Exception('Invalid signature returned (code 3).') else: logging.error( 'client.signMessageSign() returned invalid response (code 2): ' + signature.hex()) raise Exception('Invalid signature returned (code 2).') else: logging.error( 'client.signMessageSign() returned invalid response (code 1): ' + signature.hex()) raise Exception('Invalid signature returned (code 1).') return MessageSignature( pubkey.get('address').decode('ascii'), bytes(chr(27 + 4 + (signature[0] & 0x01)), "utf-8") + r + s)
def sign_tx(hw_session: HWSessionBase, rt_data: AppRuntimeData, utxos_to_spend: List[wallet_common.UtxoType], tx_outputs: List[wallet_common.TxOutputType], tx_fee): """ Creates a signed transaction. :param hw_session: :param utxos_to_spend: list of utxos to send :param tx_outputs: list of transaction outputs :param tx_fee: transaction fee :return: tuple (serialized tx, total transaction amount in satoshis) """ def load_prev_txes(tx_api_, skip_cache_: bool = False): txes_ = {} tx_api_.skip_cache = skip_cache_ for utxo_ in utxos_to_spend: prev_hash_bin = bytes.fromhex(utxo_.txid) if prev_hash_bin not in txes_: tx = tx_api_.get_tx(utxo_.txid) txes_[prev_hash_bin] = tx return txes_ insight_network = 'insight_dash' if rt_data.is_testnet: insight_network += '_testnet' dash_network = rt_data.dash_network tx_api = MyTxApiInsight(rt_data.dashd_intf, rt_data.tx_cache_dir) client = hw_session.hw_client inputs = [] outputs = [] inputs_amount = 0 for utxo_index, utxo in enumerate(utxos_to_spend): if not utxo.bip32_path: raise Exception('No BIP32 path for UTXO ' + utxo.txid) address_n = dash_utils.bip32_path_string_to_n(utxo.bip32_path) it = trezor_proto.TxInputType(address_n=address_n, amount=utxo.satoshis, prev_hash=binascii.unhexlify(utxo.txid), prev_index=int(utxo.output_index)) inputs.append(it) inputs_amount += utxo.satoshis outputs_amount = 0 for out in tx_outputs: outputs_amount += out.satoshis if out.address[0] in dash_utils.get_chain_params( dash_network).B58_PREFIXES_SCRIPT_ADDRESS: stype = trezor_proto.OutputScriptType.PAYTOSCRIPTHASH logging.debug('Transaction type: PAYTOSCRIPTHASH' + str(stype)) elif out.address[0] in dash_utils.get_chain_params( dash_network).B58_PREFIXES_PUBKEY_ADDRESS: stype = trezor_proto.OutputScriptType.PAYTOADDRESS logging.debug('Transaction type: PAYTOADDRESS ' + str(stype)) else: raise Exception('Invalid prefix of the destination address.') if out.bip32_path: address_n = dash_utils.bip32_path_string_to_n(out.bip32_path) else: address_n = None ot = trezor_proto.TxOutputType( address=out.address if address_n is None else None, address_n=address_n, amount=out.satoshis, script_type=stype) outputs.append(ot) if outputs_amount + tx_fee != inputs_amount: raise Exception( 'Transaction validation failure: inputs + fee != outputs') try: for skip_cache in (False, True): txes = load_prev_txes(tx_api, skip_cache) try: signed = btc.sign_tx(client, rt_data.hw_coin_name, inputs, outputs, prev_txes=txes) return signed[1], inputs_amount except exceptions.Cancelled: raise except Exception: if skip_cache: raise log.exception( 'Exception occurred while signing transaction. Turning off the transaction cache ' 'and retrying...') raise Exception('Internal error: transaction not signed') except exceptions.Cancelled: raise CancelException('Cancelled') except exceptions.TrezorFailure as e: if e.failure.message == 'Device not initialized': raise HwNotInitialized(e.failure.message) else: raise