URI: 
       tMerge pull request #4875 from matejcik/trezor-0.11 - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 1546d65ebe97aaf968936421fb2ed997b1de71e1
   DIR parent 605982a2b73e85d314c2cfb2c1180b3b093aee98
  HTML Author: ghost43 <somber.night@protonmail.com>
       Date:   Thu,  6 Dec 2018 19:38:51 +0100
       
       Merge pull request #4875 from matejcik/trezor-0.11
       
       WIP: Trezor 0.11
       Diffstat:
         M contrib/build-wine/deterministic.s… |       1 -
         M contrib/osx/osx.spec                |       1 -
         M electrum/plugins/hw_wallet/plugin.… |      14 +++++++++++++-
         M electrum/plugins/trezor/__init__.py |       6 +++---
         D electrum/plugins/trezor/client.py   |      11 -----------
         M electrum/plugins/trezor/clientbase… |     368 +++++++++++++++++--------------
         M electrum/plugins/trezor/qt.py       |      75 +++++++++----------------------
         D electrum/plugins/trezor/transport.… |      95 ------------------------------
         M electrum/plugins/trezor/trezor.py   |     320 ++++++++++++++-----------------
       
       9 files changed, 381 insertions(+), 510 deletions(-)
       ---
   DIR diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec
       t@@ -57,7 +57,6 @@ a = Analysis([home+'run_electrum',
                      home+'electrum/commands.py',
                      home+'electrum/plugins/cosigner_pool/qt.py',
                      home+'electrum/plugins/email_requests/qt.py',
       -              home+'electrum/plugins/trezor/client.py',
                      home+'electrum/plugins/trezor/qt.py',
                      home+'electrum/plugins/safe_t/client.py',
                      home+'electrum/plugins/safe_t/qt.py',
   DIR diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec
       t@@ -63,7 +63,6 @@ a = Analysis([electrum+ MAIN_SCRIPT,
                      electrum+'electrum/commands.py',
                      electrum+'electrum/plugins/cosigner_pool/qt.py',
                      electrum+'electrum/plugins/email_requests/qt.py',
       -              electrum+'electrum/plugins/trezor/client.py',
                      electrum+'electrum/plugins/trezor/qt.py',
                      electrum+'electrum/plugins/safe_t/client.py',
                      electrum+'electrum/plugins/safe_t/qt.py',
   DIR diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py
       t@@ -90,6 +90,9 @@ class HW_PluginBase(BasePlugin):
                raise NotImplementedError()
        
            def check_libraries_available(self) -> bool:
       +        def version_str(t):
       +            return ".".join(str(i) for i in t)
       +
                try:
                    library_version = self.get_library_version()
                except ImportError:
       t@@ -99,9 +102,18 @@ class HW_PluginBase(BasePlugin):
                    self.libraries_available_message = (
                            _("Library version for '{}' is too old.").format(self.name)
                            + '\nInstalled: {}, Needed: {}'
       -                    .format(library_version, self.minimum_library))
       +                    .format(library_version, version_str(self.minimum_library)))
                    self.print_stderr(self.libraries_available_message)
                    return False
       +        elif hasattr(self, "maximum_library") and \
       +                versiontuple(library_version) >= self.maximum_library:
       +            self.libraries_available_message = (
       +                    _("Library version for '{}' is incompatible.").format(self.name)
       +                    + '\nInstalled: {}, Needed: less than {}'
       +                    .format(library_version, version_str(self.maximum_library)))
       +            self.print_stderr(self.libraries_available_message)
       +            return False
       +
                return True
        
            def get_library_not_available_message(self) -> str:
   DIR diff --git a/electrum/plugins/trezor/__init__.py b/electrum/plugins/trezor/__init__.py
       t@@ -1,8 +1,8 @@
        from electrum.i18n import _
        
       -fullname = 'TREZOR Wallet'
       -description = _('Provides support for TREZOR hardware wallet')
       +fullname = 'Trezor Wallet'
       +description = _('Provides support for Trezor hardware wallet')
        requires = [('trezorlib','github.com/trezor/python-trezor')]
       -registers_keystore = ('hardware', 'trezor', _("TREZOR wallet"))
       +registers_keystore = ('hardware', 'trezor', _("Trezor wallet"))
        available_for = ['qt', 'cmdline']
        
   DIR diff --git a/electrum/plugins/trezor/client.py b/electrum/plugins/trezor/client.py
       t@@ -1,11 +0,0 @@
       -from trezorlib.client import proto, BaseClient, ProtocolMixin
       -from .clientbase import TrezorClientBase
       -
       -class TrezorClient(TrezorClientBase, ProtocolMixin, BaseClient):
       -    def __init__(self, transport, handler, plugin):
       -        BaseClient.__init__(self, transport=transport)
       -        ProtocolMixin.__init__(self, transport=transport)
       -        TrezorClientBase.__init__(self, handler, plugin, proto)
       -
       -
       -TrezorClientBase.wrap_methods(TrezorClient)
   DIR diff --git a/electrum/plugins/trezor/clientbase.py b/electrum/plugins/trezor/clientbase.py
       t@@ -2,120 +2,80 @@ import time
        from struct import pack
        
        from electrum.i18n import _
       -from electrum.util import PrintError, UserCancelled
       +from electrum.util import PrintError, UserCancelled, UserFacingException
        from electrum.keystore import bip39_normalize_passphrase
       -from electrum.bip32 import serialize_xpub, convert_bip32_path_to_list_of_uint32
       -
       -
       -class GuiMixin(object):
       -    # Requires: self.proto, self.device
       -
       -    # ref: https://github.com/trezor/trezor-common/blob/44dfb07cfaafffada4b2ce0d15ba1d90d17cf35e/protob/types.proto#L89
       -    messages = {
       -        3: _("Confirm the transaction output on your {} device"),
       -        4: _("Confirm internal entropy on your {} device to begin"),
       -        5: _("Write down the seed word shown on your {}"),
       -        6: _("Confirm on your {} that you want to wipe it clean"),
       -        7: _("Confirm on your {} device the message to sign"),
       -        8: _("Confirm the total amount spent and the transaction fee on your "
       -             "{} device"),
       -        10: _("Confirm wallet address on your {} device"),
       -        14: _("Choose on your {} device where to enter your passphrase"),
       -        'default': _("Check your {} device to continue"),
       -    }
       -
       -    def callback_Failure(self, msg):
       -        # BaseClient's unfortunate call() implementation forces us to
       -        # raise exceptions on failure in order to unwind the stack.
       -        # However, making the user acknowledge they cancelled
       -        # gets old very quickly, so we suppress those.  The NotInitialized
       -        # one is misnamed and indicates a passphrase request was cancelled.
       -        if msg.code in (self.types.FailureType.PinCancelled,
       -                        self.types.FailureType.ActionCancelled,
       -                        self.types.FailureType.NotInitialized):
       -            raise UserCancelled()
       -        raise RuntimeError(msg.message)
       -
       -    def callback_ButtonRequest(self, msg):
       -        message = self.msg
       -        if not message:
       -            message = self.messages.get(msg.code, self.messages['default'])
       -        self.handler.show_message(message.format(self.device), self.cancel)
       -        return self.proto.ButtonAck()
       -
       -    def callback_PinMatrixRequest(self, msg):
       -        if msg.type == 2:
       -            msg = _("Enter a new PIN for your {}:")
       -        elif msg.type == 3:
       -            msg = (_("Re-enter the new PIN for your {}.\n\n"
       -                     "NOTE: the positions of the numbers have changed!"))
       -        else:
       -            msg = _("Enter your current {} PIN:")
       -        pin = self.handler.get_pin(msg.format(self.device))
       -        if len(pin) > 9:
       -            self.handler.show_error(_('The PIN cannot be longer than 9 characters.'))
       -            pin = ''  # to cancel below
       -        if not pin:
       -            return self.proto.Cancel()
       -        return self.proto.PinMatrixAck(pin=pin)
       +from electrum.bip32 import serialize_xpub, convert_bip32_path_to_list_of_uint32 as parse_path
       +
       +from trezorlib.client import TrezorClient
       +from trezorlib.exceptions import TrezorFailure, Cancelled, OutdatedFirmwareError
       +from trezorlib.messages import WordRequestType, FailureType, RecoveryDeviceType
       +import trezorlib.btc
       +import trezorlib.device
       +
       +MESSAGES = {
       +    3: _("Confirm the transaction output on your {} device"),
       +    4: _("Confirm internal entropy on your {} device to begin"),
       +    5: _("Write down the seed word shown on your {}"),
       +    6: _("Confirm on your {} that you want to wipe it clean"),
       +    7: _("Confirm on your {} device the message to sign"),
       +    8: _("Confirm the total amount spent and the transaction fee on your {} device"),
       +    10: _("Confirm wallet address on your {} device"),
       +    14: _("Choose on your {} device where to enter your passphrase"),
       +    'default': _("Check your {} device to continue"),
       +}
       +
       +
       +class TrezorClientBase(PrintError):
       +    def __init__(self, transport, handler, plugin):
       +        self.client = TrezorClient(transport, ui=self)
       +        self.plugin = plugin
       +        self.device = plugin.device
       +        self.handler = handler
        
       -    def callback_PassphraseRequest(self, req):
       -        if req and hasattr(req, 'on_device') and req.on_device is True:
       -            return self.proto.PassphraseAck()
       +        self.msg = None
       +        self.creating_wallet = False
        
       -        if self.creating_wallet:
       -            msg = _("Enter a passphrase to generate this wallet.  Each time "
       -                    "you use this wallet your {} will prompt you for the "
       -                    "passphrase.  If you forget the passphrase you cannot "
       -                    "access the bitcoins in the wallet.").format(self.device)
       -        else:
       -            msg = _("Enter the passphrase to unlock this wallet:")
       -        passphrase = self.handler.get_passphrase(msg, self.creating_wallet)
       -        if passphrase is None:
       -            return self.proto.Cancel()
       -        passphrase = bip39_normalize_passphrase(passphrase)
       +        self.in_flow = False
        
       -        ack = self.proto.PassphraseAck(passphrase=passphrase)
       -        length = len(ack.passphrase)
       -        if length > 50:
       -            self.handler.show_error(_("Too long passphrase ({} > 50 chars).").format(length))
       -            return self.proto.Cancel()
       -        return ack
       -
       -    def callback_PassphraseStateRequest(self, msg):
       -        return self.proto.PassphraseStateAck()
       -
       -    def callback_WordRequest(self, msg):
       -        if (msg.type is not None
       -            and msg.type in (self.types.WordRequestType.Matrix9,
       -                             self.types.WordRequestType.Matrix6)):
       -            num = 9 if msg.type == self.types.WordRequestType.Matrix9 else 6
       -            char = self.handler.get_matrix(num)
       -            if char == 'x':
       -                return self.proto.Cancel()
       -            return self.proto.WordAck(word=char)
       -
       -        self.step += 1
       -        msg = _("Step {}/24.  Enter seed word as explained on "
       -                "your {}:").format(self.step, self.device)
       -        word = self.handler.get_word(msg)
       -        # Unfortunately the device can't handle self.proto.Cancel()
       -        return self.proto.WordAck(word=word)
       -
       -
       -class TrezorClientBase(GuiMixin, PrintError):
       -
       -    def __init__(self, handler, plugin, proto):
       -        assert hasattr(self, 'tx_api')  # ProtocolMixin already constructed?
       -        self.proto = proto
       -        self.device = plugin.device
       -        self.handler = handler
       -        self.tx_api = plugin
       -        self.types = plugin.types
       +        self.used()
       +
       +    def run_flow(self, message=None, creating_wallet=False):
       +        if self.in_flow:
       +            raise RuntimeError("Overlapping call to run_flow")
       +
       +        self.in_flow = True
       +        self.msg = message
       +        self.creating_wallet = creating_wallet
       +        self.prevent_timeouts()
       +        return self
       +
       +    def end_flow(self):
       +        self.in_flow = False
                self.msg = None
                self.creating_wallet = False
       +        self.handler.finished()
                self.used()
        
       +    def __enter__(self):
       +        return self
       +
       +    def __exit__(self, exc_type, exc_value, traceback):
       +        self.end_flow()
       +        if exc_value is not None:
       +            if issubclass(exc_type, Cancelled):
       +                raise UserCancelled from exc_value
       +            elif issubclass(exc_type, TrezorFailure):
       +                raise RuntimeError(exc_value.message) from exc_value
       +            elif issubclass(exc_type, OutdatedFirmwareError):
       +                raise UserFacingException(exc_value) from exc_value
       +            else:
       +                return False
       +        return True
       +
       +    @property
       +    def features(self):
       +        return self.client.features
       +
            def __str__(self):
                return "%s/%s" % (self.label(), self.features.device_id)
        
       t@@ -131,8 +91,11 @@ class TrezorClientBase(GuiMixin, PrintError):
                return not self.features.bootloader_mode
        
            def has_usable_connection_with_device(self):
       +        if self.in_flow:
       +            return True
       +
                try:
       -            res = self.ping("electrum pinging device")
       +            res = self.client.ping("electrum pinging device")
                    assert res == "electrum pinging device"
                except BaseException:
                    return False
       t@@ -150,47 +113,41 @@ class TrezorClientBase(GuiMixin, PrintError):
                    self.print_error("timed out")
                    self.clear_session()
        
       -    @staticmethod
       -    def expand_path(n):
       -        return convert_bip32_path_to_list_of_uint32(n)
       -
       -    def cancel(self):
       -        '''Provided here as in keepkeylib but not trezorlib.'''
       -        self.transport.write(self.proto.Cancel())
       -
            def i4b(self, x):
                return pack('>I', x)
        
       -    def get_xpub(self, bip32_path, xtype):
       -        address_n = self.expand_path(bip32_path)
       -        creating = False
       -        node = self.get_public_node(address_n, creating).node
       +    def get_xpub(self, bip32_path, xtype, creating=False):
       +        address_n = parse_path(bip32_path)
       +        with self.run_flow(creating_wallet=creating):
       +            node = trezorlib.btc.get_public_node(self.client, address_n).node
                return serialize_xpub(xtype, node.chain_code, node.public_key, node.depth, self.i4b(node.fingerprint), self.i4b(node.child_num))
        
            def toggle_passphrase(self):
                if self.features.passphrase_protection:
       -            self.msg = _("Confirm on your {} device to disable passphrases")
       +            msg = _("Confirm on your {} device to disable passphrases")
                else:
       -            self.msg = _("Confirm on your {} device to enable passphrases")
       +            msg = _("Confirm on your {} device to enable passphrases")
                enabled = not self.features.passphrase_protection
       -        self.apply_settings(use_passphrase=enabled)
       +        with self.run_flow(msg):
       +            trezorlib.device.apply_settings(self.client, use_passphrase=enabled)
        
            def change_label(self, label):
       -        self.msg = _("Confirm the new label on your {} device")
       -        self.apply_settings(label=label)
       +        with self.run_flow(_("Confirm the new label on your {} device")):
       +            trezorlib.device.apply_settings(self.client, label=label)
        
            def change_homescreen(self, homescreen):
       -        self.msg = _("Confirm on your {} device to change your home screen")
       -        self.apply_settings(homescreen=homescreen)
       +        with self.run_flow(_("Confirm on your {} device to change your home screen")):
       +            trezorlib.device.apply_settings(self.client, homescreen=homescreen)
        
            def set_pin(self, remove):
                if remove:
       -            self.msg = _("Confirm on your {} device to disable PIN protection")
       +            msg = _("Confirm on your {} device to disable PIN protection")
                elif self.features.pin_protection:
       -            self.msg = _("Confirm on your {} device to change your PIN")
       +            msg = _("Confirm on your {} device to change your PIN")
                else:
       -            self.msg = _("Confirm on your {} device to set a PIN")
       -        self.change_pin(remove)
       +            msg = _("Confirm on your {} device to set a PIN")
       +        with self.run_flow(msg):
       +            trezorlib.device.change_pin(remove)
        
            def clear_session(self):
                '''Clear the session to force pin (and passphrase if enabled)
       t@@ -198,54 +155,131 @@ class TrezorClientBase(GuiMixin, PrintError):
                self.print_error("clear session:", self)
                self.prevent_timeouts()
                try:
       -            super(TrezorClientBase, self).clear_session()
       +            self.client.clear_session()
                except BaseException as e:
                    # If the device was removed it has the same effect...
                    self.print_error("clear_session: ignoring error", str(e))
        
       -    def get_public_node(self, address_n, creating):
       -        self.creating_wallet = creating
       -        return super(TrezorClientBase, self).get_public_node(address_n)
       -
            def close(self):
                '''Called when Our wallet was closed or the device removed.'''
                self.print_error("closing client")
                self.clear_session()
       -        # Release the device
       -        self.transport.close()
       -
       -    def firmware_version(self):
       -        f = self.features
       -        return (f.major_version, f.minor_version, f.patch_version)
        
       -    def atleast_version(self, major, minor=0, patch=0):
       -        return self.firmware_version() >= (major, minor, patch)
       +    def is_uptodate(self):
       +        if self.client.is_outdated():
       +            return False
       +        return self.client.version >= self.plugin.minimum_firmware
        
            def get_trezor_model(self):
                """Returns '1' for Trezor One, 'T' for Trezor T."""
                return self.features.model
        
       -    @staticmethod
       -    def wrapper(func):
       -        '''Wrap methods to clear any message box they opened.'''
       -
       -        def wrapped(self, *args, **kwargs):
       -            try:
       -                self.prevent_timeouts()
       -                return func(self, *args, **kwargs)
       -            finally:
       -                self.used()
       -                self.handler.finished()
       -                self.creating_wallet = False
       -                self.msg = None
       -
       -        return wrapped
       -
       -    @staticmethod
       -    def wrap_methods(cls):
       -        for method in ['apply_settings', 'change_pin',
       -                       'get_address', 'get_public_node',
       -                       'load_device_by_mnemonic', 'load_device_by_xprv',
       -                       'recovery_device', 'reset_device', 'sign_message',
       -                       'sign_tx', 'wipe_device']:
       -            setattr(cls, method, cls.wrapper(getattr(cls, method)))
       +    def show_address(self, address_str, script_type, multisig=None):
       +        coin_name = self.plugin.get_coin_name()
       +        address_n = parse_path(address_str)
       +        with self.run_flow():
       +            return trezorlib.btc.get_address(
       +                self.client,
       +                coin_name,
       +                address_n,
       +                show_display=True,
       +                script_type=script_type,
       +                multisig=multisig)
       +
       +    def sign_message(self, address_str, message):
       +        coin_name = self.plugin.get_coin_name()
       +        address_n = parse_path(address_str)
       +        with self.run_flow():
       +            return trezorlib.btc.sign_message(
       +                self.client,
       +                coin_name,
       +                address_n,
       +                message)
       +
       +    def recover_device(self, recovery_type, *args, **kwargs):
       +        input_callback = self.mnemonic_callback(recovery_type)
       +        with self.run_flow():
       +            return trezorlib.device.recover(
       +                self.client,
       +                *args,
       +                input_callback=input_callback,
       +                **kwargs)
       +
       +    # ========= Unmodified trezorlib methods =========
       +
       +    def sign_tx(self, *args, **kwargs):
       +        with self.run_flow():
       +            return trezorlib.btc.sign_tx(self.client, *args, **kwargs)
       +
       +    def reset_device(self, *args, **kwargs):
       +        with self.run_flow():
       +            return trezorlib.device.reset(self.client, *args, **kwargs)
       +
       +    def wipe_device(self, *args, **kwargs):
       +        with self.run_flow():
       +            return trezorlib.device.wipe(self.client, *args, **kwargs)
       +
       +    # ========= UI methods ==========
       +
       +    def button_request(self, code):
       +        message = self.msg or MESSAGES.get(code) or MESSAGES['default']
       +        self.handler.show_message(message.format(self.device), self.client.cancel)
       +
       +    def get_pin(self, code=None):
       +        if code == 2:
       +            msg = _("Enter a new PIN for your {}:")
       +        elif code == 3:
       +            msg = (_("Re-enter the new PIN for your {}.\n\n"
       +                     "NOTE: the positions of the numbers have changed!"))
       +        else:
       +            msg = _("Enter your current {} PIN:")
       +        pin = self.handler.get_pin(msg.format(self.device))
       +        if not pin:
       +            raise Cancelled
       +        if len(pin) > 9:
       +            self.handler.show_error(_('The PIN cannot be longer than 9 characters.'))
       +            raise Cancelled
       +        return pin
       +
       +    def get_passphrase(self):
       +        if self.creating_wallet:
       +            msg = _("Enter a passphrase to generate this wallet.  Each time "
       +                    "you use this wallet your {} will prompt you for the "
       +                    "passphrase.  If you forget the passphrase you cannot "
       +                    "access the bitcoins in the wallet.").format(self.device)
       +        else:
       +            msg = _("Enter the passphrase to unlock this wallet:")
       +        passphrase = self.handler.get_passphrase(msg, self.creating_wallet)
       +        if passphrase is None:
       +            raise Cancelled
       +        passphrase = bip39_normalize_passphrase(passphrase)
       +        length = len(passphrase)
       +        if length > 50:
       +            self.handler.show_error(_("Too long passphrase ({} > 50 chars).").format(length))
       +            raise Cancelled
       +        return passphrase
       +
       +    def _matrix_char(self, matrix_type):
       +        num = 9 if matrix_type == WordRequestType.Matrix9 else 6
       +        char = self.handler.get_matrix(num)
       +        if char == 'x':
       +            raise Cancelled
       +        return char
       +
       +    def mnemonic_callback(self, recovery_type):
       +        if recovery_type is None:
       +            return None
       +
       +        if recovery_type == RecoveryDeviceType.Matrix:
       +            return self._matrix_char
       +
       +        step = 0
       +        def word_callback(_ignored):
       +            nonlocal step
       +            step += 1
       +            msg = _("Step {}/24.  Enter seed word as explained on your {}:").format(step, self.device)
       +            word = self.handler.get_word(msg)
       +            if not word:
       +                raise Cancelled
       +            return word
       +        return word_callback
   DIR diff --git a/electrum/plugins/trezor/qt.py b/electrum/plugins/trezor/qt.py
       t@@ -12,7 +12,7 @@ from electrum.util import bh2u
        
        from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
        from ..hw_wallet.plugin import only_hook_if_libraries_available
       -from .trezor import (TrezorPlugin, TIM_NEW, TIM_RECOVER, TIM_MNEMONIC,
       +from .trezor import (TrezorPlugin, TIM_NEW, TIM_RECOVER,
                             RECOVERY_TYPE_SCRAMBLED_WORDS, RECOVERY_TYPE_MATRIX)
        
        
       t@@ -197,50 +197,24 @@ class QtPlugin(QtPluginBase):
                    text = widget.toPlainText().strip()
                    return ' '.join(text.split())
        
       -        if method in [TIM_NEW, TIM_RECOVER]:
       -            gb = QGroupBox()
       -            hbox1 = QHBoxLayout()
       -            gb.setLayout(hbox1)
       -            vbox.addWidget(gb)
       -            gb.setTitle(_("Select your seed length:"))
       -            bg_numwords = QButtonGroup()
       -            for i, count in enumerate([12, 18, 24]):
       -                rb = QRadioButton(gb)
       -                rb.setText(_("%d words") % count)
       -                bg_numwords.addButton(rb)
       -                bg_numwords.setId(rb, i)
       -                hbox1.addWidget(rb)
       -                rb.setChecked(True)
       -            cb_pin = QCheckBox(_('Enable PIN protection'))
       -            cb_pin.setChecked(True)
       -        else:
       -            text = QTextEdit()
       -            text.setMaximumHeight(60)
       -            if method == TIM_MNEMONIC:
       -                msg = _("Enter your BIP39 mnemonic:")
       -            else:
       -                msg = _("Enter the master private key beginning with xprv:")
       -                def set_enabled():
       -                    from electrum.bip32 import is_xprv
       -                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))
       -                text.textChanged.connect(set_enabled)
       -                next_enabled = False
       -
       -            vbox.addWidget(QLabel(msg))
       -            vbox.addWidget(text)
       -            pin = QLineEdit()
       -            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
       -            pin.setMaximumWidth(100)
       -            hbox_pin = QHBoxLayout()
       -            hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
       -            hbox_pin.addWidget(pin)
       -            hbox_pin.addStretch(1)
       -
       -        if method in [TIM_NEW, TIM_RECOVER]:
       -            vbox.addWidget(WWLabel(RECOMMEND_PIN))
       -            vbox.addWidget(cb_pin)
       -        else:
       -            vbox.addLayout(hbox_pin)
       +        gb = QGroupBox()
       +        hbox1 = QHBoxLayout()
       +        gb.setLayout(hbox1)
       +        vbox.addWidget(gb)
       +        gb.setTitle(_("Select your seed length:"))
       +        bg_numwords = QButtonGroup()
       +        for i, count in enumerate([12, 18, 24]):
       +            rb = QRadioButton(gb)
       +            rb.setText(_("%d words") % count)
       +            bg_numwords.addButton(rb)
       +            bg_numwords.setId(rb, i)
       +            hbox1.addWidget(rb)
       +            rb.setChecked(True)
       +        cb_pin = QCheckBox(_('Enable PIN protection'))
       +        cb_pin.setChecked(True)
       +
       +        vbox.addWidget(WWLabel(RECOMMEND_PIN))
       +        vbox.addWidget(cb_pin)
        
                passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
                passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
       t@@ -277,14 +251,9 @@ class QtPlugin(QtPluginBase):
        
                wizard.exec_layout(vbox, next_enabled=next_enabled)
        
       -        if method in [TIM_NEW, TIM_RECOVER]:
       -            item = bg_numwords.checkedId()
       -            pin = cb_pin.isChecked()
       -            recovery_type = bg_rectype.checkedId() if bg_rectype else None
       -        else:
       -            item = ' '.join(str(clean_text(text)).split())
       -            pin = str(pin.text())
       -            recovery_type = None
       +        item = bg_numwords.checkedId()
       +        pin = cb_pin.isChecked()
       +        recovery_type = bg_rectype.checkedId() if bg_rectype else None
        
                return (item, name.text(), pin, cb_phrase.isChecked(), recovery_type)
        
   DIR diff --git a/electrum/plugins/trezor/transport.py b/electrum/plugins/trezor/transport.py
       t@@ -1,95 +0,0 @@
       -from electrum.util import PrintError
       -
       -
       -class TrezorTransport(PrintError):
       -
       -    @staticmethod
       -    def all_transports():
       -        """Reimplemented trezorlib.transport.all_transports so that we can
       -        enable/disable specific transports.
       -        """
       -        try:
       -            # only to detect trezorlib version
       -            from trezorlib.transport import all_transports
       -        except ImportError:
       -            # old trezorlib. compat for trezorlib < 0.9.2
       -            transports = []
       -            try:
       -               from trezorlib.transport_bridge import BridgeTransport
       -               transports.append(BridgeTransport)
       -            except BaseException:
       -               pass
       -            try:
       -                from trezorlib.transport_hid import HidTransport
       -                transports.append(HidTransport)
       -            except BaseException:
       -                pass
       -            try:
       -                from trezorlib.transport_udp import UdpTransport
       -                transports.append(UdpTransport)
       -            except BaseException:
       -                pass
       -            try:
       -                from trezorlib.transport_webusb import WebUsbTransport
       -                transports.append(WebUsbTransport)
       -            except BaseException:
       -                pass
       -        else:
       -            # new trezorlib.
       -            transports = []
       -            try:
       -               from trezorlib.transport.bridge import BridgeTransport
       -               transports.append(BridgeTransport)
       -            except BaseException:
       -               pass
       -            try:
       -                from trezorlib.transport.hid import HidTransport
       -                transports.append(HidTransport)
       -            except BaseException:
       -                pass
       -            try:
       -                from trezorlib.transport.udp import UdpTransport
       -                transports.append(UdpTransport)
       -            except BaseException:
       -                pass
       -            try:
       -                from trezorlib.transport.webusb import WebUsbTransport
       -                transports.append(WebUsbTransport)
       -            except BaseException:
       -                pass
       -            return transports
       -        return transports
       -
       -    def enumerate_devices(self):
       -        """Just like trezorlib.transport.enumerate_devices,
       -        but with exception catching, so that transports can fail separately.
       -        """
       -        devices = []
       -        for transport in self.all_transports():
       -            try:
       -                new_devices = transport.enumerate()
       -            except BaseException as e:
       -                self.print_error('enumerate failed for {}. error {}'
       -                                 .format(transport.__name__, str(e)))
       -            else:
       -                devices.extend(new_devices)
       -        return devices
       -
       -    def get_transport(self, path=None):
       -        """Reimplemented trezorlib.transport.get_transport,
       -        (1) for old trezorlib
       -        (2) to be able to disable specific transports
       -        (3) to call our own enumerate_devices that catches exceptions
       -        """
       -        if path is None:
       -            try:
       -                return self.enumerate_devices()[0]
       -            except IndexError:
       -                raise Exception("No TREZOR device found") from None
       -
       -        def match_prefix(a, b):
       -            return a.startswith(b) or b.startswith(a)
       -        transports = [t for t in self.all_transports() if match_prefix(path, t.PATH_PREFIX)]
       -        if transports:
       -            return transports[0].find_by_path(path)
       -        raise Exception("Unknown path prefix '%s'" % path)
   DIR diff --git a/electrum/plugins/trezor/trezor.py b/electrum/plugins/trezor/trezor.py
       t@@ -1,29 +1,51 @@
       -from binascii import hexlify, unhexlify
        import traceback
        import sys
        
        from electrum.util import bfh, bh2u, versiontuple, UserCancelled, UserFacingException
        from electrum.bitcoin import TYPE_ADDRESS, TYPE_SCRIPT
       -from electrum.bip32 import deserialize_xpub
       +from electrum.bip32 import deserialize_xpub, convert_bip32_path_to_list_of_uint32 as parse_path
        from electrum import constants
        from electrum.i18n import _
        from electrum.plugin import Device
        from electrum.transaction import deserialize, Transaction
        from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey
       -from electrum.base_wizard import ScriptTypeNotSupported
       +from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET
        
        from ..hw_wallet import HW_PluginBase
        from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, trezor_validate_op_return_output_and_get_data
        
       +try:
       +    import trezorlib
       +    import trezorlib.transport
        
       -# TREZOR initialization methods
       -TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
       -RECOVERY_TYPE_SCRAMBLED_WORDS, RECOVERY_TYPE_MATRIX = range(0, 2)
       +    from .clientbase import TrezorClientBase
       +
       +    from trezorlib.messages import (
       +        RecoveryDeviceType, HDNodeType, HDNodePathType,
       +        InputScriptType, OutputScriptType, MultisigRedeemScriptType,
       +        TxInputType, TxOutputType, TxOutputBinType, TransactionType, SignTx)
       +
       +    RECOVERY_TYPE_SCRAMBLED_WORDS = RecoveryDeviceType.ScrambledWords
       +    RECOVERY_TYPE_MATRIX = RecoveryDeviceType.Matrix
       +
       +    TREZORLIB = True
       +except Exception as e:
       +    import traceback
       +    traceback.print_exc()
       +    TREZORLIB = False
       +
       +    RECOVERY_TYPE_SCRAMBLED_WORDS, RECOVERY_TYPE_MATRIX = range(2)
       +
       +
       +# Trezor initialization methods
       +TIM_NEW, TIM_RECOVER = range(2)
       +
       +TREZOR_PRODUCT_KEY = 'Trezor'
        
        
        class TrezorKeyStore(Hardware_KeyStore):
            hw_type = 'trezor'
       -    device = 'TREZOR'
       +    device = TREZOR_PRODUCT_KEY
        
            def get_derivation(self):
                return self.derivation
       t@@ -37,8 +59,7 @@ class TrezorKeyStore(Hardware_KeyStore):
            def sign_message(self, sequence, message, password):
                client = self.get_client()
                address_path = self.get_derivation() + "/%d/%d"%sequence
       -        address_n = client.expand_path(address_path)
       -        msg_sig = client.sign_message(self.plugin.get_coin_name(), address_n, message)
       +        msg_sig = client.sign_message(address_path, message)
                return msg_sig.signature
        
            def sign_transaction(self, tx, password):
       t@@ -75,41 +96,35 @@ class TrezorPlugin(HW_PluginBase):
            libraries_URL = 'https://github.com/trezor/python-trezor'
            minimum_firmware = (1, 5, 2)
            keystore_class = TrezorKeyStore
       -    minimum_library = (0, 9, 0)
       +    minimum_library = (0, 11, 0)
       +    maximum_library = (0, 12)
            SUPPORTED_XTYPES = ('standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh')
       +    DEVICE_IDS = (TREZOR_PRODUCT_KEY,)
        
            MAX_LABEL_LEN = 32
        
            def __init__(self, parent, config, name):
       -        HW_PluginBase.__init__(self, parent, config, name)
       +        super().__init__(parent, config, name)
        
                self.libraries_available = self.check_libraries_available()
                if not self.libraries_available:
                    return
       -
       -        from . import client
       -        from . import transport
       -        import trezorlib.messages
       -        self.client_class = client.TrezorClient
       -        self.types = trezorlib.messages
       -        self.DEVICE_IDS = ('TREZOR',)
       -
       -        self.transport_handler = transport.TrezorTransport()
                self.device_manager().register_enumerate_func(self.enumerate)
        
            def get_library_version(self):
       -        import trezorlib
       +        if not TREZORLIB:
       +            raise ImportError
                try:
                    return trezorlib.__version__
       -        except AttributeError:
       +        except Exception:
                    return 'unknown'
        
            def enumerate(self):
       -        devices = self.transport_handler.enumerate_devices()
       +        devices = trezorlib.transport.enumerate_devices()
                return [Device(path=d.get_path(),
                               interface_number=-1,
                               id_=d.get_path(),
       -                       product_key='TREZOR',
       +                       product_key=TREZOR_PRODUCT_KEY,
                               usage_page=0,
                               transport_ui_string=d.get_path())
                        for d in devices]
       t@@ -117,7 +132,7 @@ class TrezorPlugin(HW_PluginBase):
            def create_client(self, device, handler):
                try:
                    self.print_error("connecting to device at", device.path)
       -            transport = self.transport_handler.get_transport(device.path)
       +            transport = trezorlib.transport.get_transport(device.path)
                except BaseException as e:
                    self.print_error("cannot connect at", device.path, str(e))
                    return None
       t@@ -128,27 +143,7 @@ class TrezorPlugin(HW_PluginBase):
        
                self.print_error("connected to device at", device.path)
                # note that this call can still raise!
       -        client = self.client_class(transport, handler, self)
       -
       -        # Try a ping for device sanity
       -        try:
       -            client.ping('t')
       -        except BaseException as e:
       -            self.print_error("ping failed", str(e))
       -            return None
       -
       -        if not client.atleast_version(*self.minimum_firmware):
       -            msg = (_('Outdated {} firmware for device labelled {}. Please '
       -                     'download the updated firmware from {}')
       -                   .format(self.device, client.label(), self.firmware_URL))
       -            self.print_error(msg)
       -            if handler:
       -                handler.show_error(msg)
       -            else:
       -                raise UserFacingException(msg)
       -            return None
       -
       -        return client
       +        return TrezorClientBase(transport, handler, self)
        
            def get_client(self, keystore, force_pair=True):
                devmgr = self.device_manager()
       t@@ -177,8 +172,6 @@ class TrezorPlugin(HW_PluginBase):
                    # Must be short as QT doesn't word-wrap radio button text
                    (TIM_NEW, _("Let the device generate a completely new seed randomly")),
                    (TIM_RECOVER, _("Recover from a seed you have previously written down")),
       -            (TIM_MNEMONIC, _("Upload a BIP39 mnemonic to generate the seed")),
       -            (TIM_PRIVKEY, _("Upload a master private key"))
                ]
                devmgr = self.device_manager()
                client = devmgr.client_by_id(device_id)
       t@@ -222,49 +215,37 @@ class TrezorPlugin(HW_PluginBase):
                        "the words carefully!"),
                        blocking=True)
        
       -        language = 'english'
                devmgr = self.device_manager()
                client = devmgr.client_by_id(device_id)
        
                if method == TIM_NEW:
       -            strength = 64 * (item + 2)  # 128, 192 or 256
       -            u2f_counter = 0
       -            skip_backup = False
       -            client.reset_device(True, strength, passphrase_protection,
       -                                pin_protection, label, language,
       -                                u2f_counter, skip_backup)
       +            client.reset_device(
       +                strength=64 * (item + 2),  # 128, 192 or 256
       +                passphrase_protection=passphrase_protection,
       +                pin_protection=pin_protection,
       +                label=label)
                elif method == TIM_RECOVER:
       -            word_count = 6 * (item + 2)  # 12, 18 or 24
       -            client.step = 0
       -            if recovery_type == RECOVERY_TYPE_SCRAMBLED_WORDS:
       -                recovery_type_trezor = self.types.RecoveryDeviceType.ScrambledWords
       -            else:
       -                recovery_type_trezor = self.types.RecoveryDeviceType.Matrix
       -            client.recovery_device(word_count, passphrase_protection,
       -                                   pin_protection, label, language,
       -                                   type=recovery_type_trezor)
       +            client.recover_device(
       +                recovery_type=recovery_type,
       +                word_count=6 * (item + 2),  # 12, 18 or 24
       +                passphrase_protection=passphrase_protection,
       +                pin_protection=pin_protection,
       +                label=label)
                    if recovery_type == RECOVERY_TYPE_MATRIX:
                        handler.close_matrix_dialog()
       -        elif method == TIM_MNEMONIC:
       -            pin = pin_protection  # It's the pin, not a boolean
       -            client.load_device_by_mnemonic(str(item), pin,
       -                                           passphrase_protection,
       -                                           label, language)
                else:
       -            pin = pin_protection  # It's the pin, not a boolean
       -            client.load_device_by_xprv(item, pin, passphrase_protection,
       -                                       label, language)
       +            raise RuntimeError("Unsupported recovery method")
        
            def _make_node_path(self, xpub, address_n):
                _, depth, fingerprint, child_num, chain_code, key = deserialize_xpub(xpub)
       -        node = self.types.HDNodeType(
       +        node = HDNodeType(
                    depth=depth,
                    fingerprint=int.from_bytes(fingerprint, 'big'),
                    child_num=int.from_bytes(child_num, 'big'),
                    chain_code=chain_code,
                    public_key=key,
                )
       -        return self.types.HDNodePathType(node=node, address_n=address_n)
       +        return HDNodePathType(node=node, address_n=address_n)
        
            def setup_device(self, device_info, wizard, purpose):
                devmgr = self.device_manager()
       t@@ -273,11 +254,19 @@ class TrezorPlugin(HW_PluginBase):
                if client is None:
                    raise UserFacingException(_('Failed to create a client for this device.') + '\n' +
                                              _('Make sure it is in the correct state.'))
       +
       +        if not client.is_uptodate():
       +            msg = (_('Outdated {} firmware for device labelled {}. Please '
       +                     'download the updated firmware from {}')
       +                   .format(self.device, client.label(), self.firmware_URL))
       +            raise UserFacingException(msg)
       +
                # fixme: we should use: client.handler = wizard
                client.handler = self.create_handler(wizard)
                if not device_info.initialized:
                    self.initialize_device(device_id, wizard, client.handler)
       -        client.get_xpub('m', 'standard')
       +        is_creating_wallet = purpose == HWD_SETUP_NEW_WALLET
       +        client.get_xpub('m', 'standard', creating=is_creating_wallet)
                client.used()
        
            def get_xpub(self, device_id, derivation, xtype, wizard):
       t@@ -292,33 +281,33 @@ class TrezorPlugin(HW_PluginBase):
        
            def get_trezor_input_script_type(self, electrum_txin_type: str):
                if electrum_txin_type in ('p2wpkh', 'p2wsh'):
       -            return self.types.InputScriptType.SPENDWITNESS
       +            return InputScriptType.SPENDWITNESS
                if electrum_txin_type in ('p2wpkh-p2sh', 'p2wsh-p2sh'):
       -            return self.types.InputScriptType.SPENDP2SHWITNESS
       +            return InputScriptType.SPENDP2SHWITNESS
                if electrum_txin_type in ('p2pkh', ):
       -            return self.types.InputScriptType.SPENDADDRESS
       +            return InputScriptType.SPENDADDRESS
                if electrum_txin_type in ('p2sh', ):
       -            return self.types.InputScriptType.SPENDMULTISIG
       +            return InputScriptType.SPENDMULTISIG
                raise ValueError('unexpected txin type: {}'.format(electrum_txin_type))
        
            def get_trezor_output_script_type(self, electrum_txin_type: str):
                if electrum_txin_type in ('p2wpkh', 'p2wsh'):
       -            return self.types.OutputScriptType.PAYTOWITNESS
       +            return OutputScriptType.PAYTOWITNESS
                if electrum_txin_type in ('p2wpkh-p2sh', 'p2wsh-p2sh'):
       -            return self.types.OutputScriptType.PAYTOP2SHWITNESS
       +            return OutputScriptType.PAYTOP2SHWITNESS
                if electrum_txin_type in ('p2pkh', ):
       -            return self.types.OutputScriptType.PAYTOADDRESS
       +            return OutputScriptType.PAYTOADDRESS
                if electrum_txin_type in ('p2sh', ):
       -            return self.types.OutputScriptType.PAYTOMULTISIG
       +            return OutputScriptType.PAYTOMULTISIG
                raise ValueError('unexpected txin type: {}'.format(electrum_txin_type))
        
            def sign_transaction(self, keystore, tx, prev_tx, xpub_path):
       -        self.prev_tx = prev_tx
       -        self.xpub_path = xpub_path
       +        prev_tx = { bfh(txhash): self.electrum_tx_to_txtype(tx, xpub_path) for txhash, tx in prev_tx.items() }
                client = self.get_client(keystore)
       -        inputs = self.tx_inputs(tx, True)
       +        inputs = self.tx_inputs(tx, xpub_path, True)
                outputs = self.tx_outputs(keystore.get_derivation(), tx)
       -        signatures = client.sign_tx(self.get_coin_name(), inputs, outputs, lock_time=tx.locktime)[0]
       +        details = SignTx(lock_time=tx.locktime)
       +        signatures, _ = client.sign_tx(self.get_coin_name(), inputs, outputs, details=details, prev_txes=prev_tx)
                signatures = [(bh2u(x) + '01') for x in signatures]
                tx.update_signatures(signatures)
        
       t@@ -327,74 +316,50 @@ class TrezorPlugin(HW_PluginBase):
                    keystore = wallet.get_keystore()
                if not self.show_address_helper(wallet, address, keystore):
                    return
       -        client = self.get_client(keystore)
       -        if not client.atleast_version(1, 3):
       -            keystore.handler.show_error(_("Your device firmware is too old"))
       -            return
       -        change, index = wallet.get_address_index(address)
       +        deriv_suffix = wallet.get_address_index(address)
                derivation = keystore.derivation
       -        address_path = "%s/%d/%d"%(derivation, change, index)
       -        address_n = client.expand_path(address_path)
       +        address_path = "%s/%d/%d"%(derivation, *deriv_suffix)
       +        script_type = self.get_trezor_input_script_type(wallet.txin_type)
       +
       +        # prepare multisig, if available:
                xpubs = wallet.get_master_public_keys()
       -        if len(xpubs) == 1:
       -            script_type = self.get_trezor_input_script_type(wallet.txin_type)
       -            client.get_address(self.get_coin_name(), address_n, True, script_type=script_type)
       -        else:
       -            def f(xpub):
       -                return self._make_node_path(xpub, [change, index])
       +        if len(xpubs) > 1:
                    pubkeys = wallet.get_public_keys(address)
                    # sort xpubs using the order of pubkeys
       -            sorted_pubkeys, sorted_xpubs = zip(*sorted(zip(pubkeys, xpubs)))
       -            pubkeys = list(map(f, sorted_xpubs))
       -            multisig = self.types.MultisigRedeemScriptType(
       -               pubkeys=pubkeys,
       -               signatures=[b''] * wallet.n,
       -               m=wallet.m,
       -            )
       -            script_type = self.get_trezor_input_script_type(wallet.txin_type)
       -            client.get_address(self.get_coin_name(), address_n, True, multisig=multisig, script_type=script_type)
       -
       -    def tx_inputs(self, tx, for_sig=False):
       +            sorted_pairs = sorted(zip(pubkeys, xpubs))
       +            multisig = self._make_multisig(
       +                wallet.m,
       +                [(xpub, deriv_suffix) for _, xpub in sorted_pairs])
       +        else:
       +            multisig = None
       +
       +        client = self.get_client(keystore)
       +        client.show_address(address_path, script_type, multisig)
       +
       +    def tx_inputs(self, tx, xpub_path, for_sig=False):
                inputs = []
                for txin in tx.inputs():
       -            txinputtype = self.types.TxInputType()
       +            txinputtype = TxInputType()
                    if txin['type'] == 'coinbase':
                        prev_hash = b"\x00"*32
                        prev_index = 0xffffffff  # signed int -1
                    else:
                        if for_sig:
                            x_pubkeys = txin['x_pubkeys']
       -                    if len(x_pubkeys) == 1:
       -                        x_pubkey = x_pubkeys[0]
       -                        xpub, s = parse_xpubkey(x_pubkey)
       -                        xpub_n = self.client_class.expand_path(self.xpub_path[xpub])
       -                        txinputtype._extend_address_n(xpub_n + s)
       -                        txinputtype.script_type = self.get_trezor_input_script_type(txin['type'])
       -                    else:
       -                        def f(x_pubkey):
       -                            xpub, s = parse_xpubkey(x_pubkey)
       -                            return self._make_node_path(xpub, s)
       -                        pubkeys = list(map(f, x_pubkeys))
       -                        multisig = self.types.MultisigRedeemScriptType(
       -                            pubkeys=pubkeys,
       -                            signatures=list(map(lambda x: bfh(x)[:-1] if x else b'', txin.get('signatures'))),
       -                            m=txin.get('num_sig'),
       -                        )
       -                        script_type = self.get_trezor_input_script_type(txin['type'])
       -                        txinputtype = self.types.TxInputType(
       -                            script_type=script_type,
       -                            multisig=multisig
       -                        )
       -                        # find which key is mine
       -                        for x_pubkey in x_pubkeys:
       -                            if is_xpubkey(x_pubkey):
       -                                xpub, s = parse_xpubkey(x_pubkey)
       -                                if xpub in self.xpub_path:
       -                                    xpub_n = self.client_class.expand_path(self.xpub_path[xpub])
       -                                    txinputtype._extend_address_n(xpub_n + s)
       -                                    break
       -
       -                prev_hash = unhexlify(txin['prevout_hash'])
       +                    xpubs = [parse_xpubkey(x) for x in x_pubkeys]
       +                    multisig = self._make_multisig(txin.get('num_sig'), xpubs, txin.get('signatures'))
       +                    script_type = self.get_trezor_input_script_type(txin['type'])
       +                    txinputtype = TxInputType(
       +                        script_type=script_type,
       +                        multisig=multisig)
       +                    # find which key is mine
       +                    for xpub, deriv in xpubs:
       +                        if xpub in xpub_path:
       +                            xpub_n = parse_path(xpub_path[xpub])
       +                            txinputtype.address_n = xpub_n + deriv
       +                            break
       +
       +                prev_hash = bfh(txin['prevout_hash'])
                        prev_index = txin['prevout_n']
        
                    if 'value' in txin:
       t@@ -412,39 +377,44 @@ class TrezorPlugin(HW_PluginBase):
        
                return inputs
        
       +    def _make_multisig(self, m, xpubs, signatures=None):
       +        if len(xpubs) == 1:
       +            return None
       +
       +        pubkeys = [self._make_node_path(xpub, deriv) for xpub, deriv in xpubs]
       +        if signatures is None:
       +            signatures = [b''] * len(pubkeys)
       +        elif len(signatures) != len(pubkeys):
       +            raise RuntimeError('Mismatched number of signatures')
       +        else:
       +            signatures = [bfh(x)[:-1] if x else b'' for x in signatures]
       +
       +        return MultisigRedeemScriptType(
       +            pubkeys=pubkeys,
       +            signatures=signatures,
       +            m=m)
       +
            def tx_outputs(self, derivation, tx):
        
                def create_output_by_derivation():
                    script_type = self.get_trezor_output_script_type(info.script_type)
       -            if len(xpubs) == 1:
       -                address_n = self.client_class.expand_path(derivation + "/%d/%d" % index)
       -                txoutputtype = self.types.TxOutputType(
       -                    amount=amount,
       -                    script_type=script_type,
       -                    address_n=address_n,
       -                )
       -            else:
       -                address_n = self.client_class.expand_path("/%d/%d" % index)
       -                pubkeys = [self._make_node_path(xpub, address_n) for xpub in xpubs]
       -                multisig = self.types.MultisigRedeemScriptType(
       -                    pubkeys=pubkeys,
       -                    signatures=[b''] * len(pubkeys),
       -                    m=m)
       -                txoutputtype = self.types.TxOutputType(
       -                    multisig=multisig,
       -                    amount=amount,
       -                    address_n=self.client_class.expand_path(derivation + "/%d/%d" % index),
       -                    script_type=script_type)
       +            deriv = parse_path("/%d/%d" % index)
       +            multisig = self._make_multisig(m, [(xpub, deriv) for xpub in xpubs])
       +            txoutputtype = TxOutputType(
       +                multisig=multisig,
       +                amount=amount,
       +                address_n=parse_path(derivation + "/%d/%d" % index),
       +                script_type=script_type)
                    return txoutputtype
        
                def create_output_by_address():
       -            txoutputtype = self.types.TxOutputType()
       +            txoutputtype = TxOutputType()
                    txoutputtype.amount = amount
                    if _type == TYPE_SCRIPT:
       -                txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
       +                txoutputtype.script_type = OutputScriptType.PAYTOOPRETURN
                        txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(o)
                    elif _type == TYPE_ADDRESS:
       -                txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
       +                txoutputtype.script_type = OutputScriptType.PAYTOADDRESS
                        txoutputtype.address = address
                    return txoutputtype
        
       t@@ -476,23 +446,17 @@ class TrezorPlugin(HW_PluginBase):
        
                return outputs
        
       -    def electrum_tx_to_txtype(self, tx):
       -        t = self.types.TransactionType()
       +    def electrum_tx_to_txtype(self, tx, xpub_path):
       +        t = TransactionType()
                if tx is None:
                    # probably for segwit input and we don't need this prev txn
                    return t
                d = deserialize(tx.raw)
                t.version = d['version']
                t.lock_time = d['lockTime']
       -        inputs = self.tx_inputs(tx)
       -        t._extend_inputs(inputs)
       -        for vout in d['outputs']:
       -            o = t._add_bin_outputs()
       -            o.amount = vout['value']
       -            o.script_pubkey = bfh(vout['scriptPubKey'])
       +        t.inputs = self.tx_inputs(tx, xpub_path)
       +        t.bin_outputs = [
       +            TxOutputBinType(amount=vout['value'], script_pubkey=bfh(vout['scriptPubKey']))
       +            for vout in d['outputs']
       +        ]
                return t
       -
       -    # This function is called from the TREZOR libraries (via tx_api)
       -    def get_tx(self, tx_hash):
       -        tx = self.prev_tx[tx_hash]
       -        return self.electrum_tx_to_txtype(tx)