URI: 
       tkeepkey: full segwit support - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 8aebb8249a0163cb2b1953b6aeaab9ff0c569f61
   DIR parent 70c32590a9bdfccc1ff6896d0fc558fb163c203a
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Sun, 30 Sep 2018 01:29:27 +0200
       
       keepkey: full segwit support
       
       ported from trezor plugin
       needs new fw to work (5.8??)
       
       fixes #3462
       
       Diffstat:
         M electrum/plugins/keepkey/keepkey.py |     112 ++++++++++++++++++++-----------
         M electrum/plugins/keepkey/qt.py      |      13 +++++++------
         M electrum/plugins/trezor/trezor.py   |       4 ++--
       
       3 files changed, 81 insertions(+), 48 deletions(-)
       ---
   DIR diff --git a/electrum/plugins/keepkey/keepkey.py b/electrum/plugins/keepkey/keepkey.py
       t@@ -3,9 +3,8 @@ import traceback
        import sys
        
        from electrum.util import bfh, bh2u, UserCancelled
       -from electrum.bitcoin import (b58_address_to_hash160, xpub_from_pubkey,
       -                              TYPE_ADDRESS, TYPE_SCRIPT,
       -                              is_segwit_address)
       +from electrum.bitcoin import (xpub_from_pubkey, deserialize_xpub,
       +                              TYPE_ADDRESS, TYPE_SCRIPT)
        from electrum import constants
        from electrum.i18n import _
        from electrum.plugin import BasePlugin
       t@@ -29,9 +28,6 @@ class KeepKey_KeyStore(Hardware_KeyStore):
            def get_derivation(self):
                return self.derivation
        
       -    def is_segwit(self):
       -        return self.derivation.startswith("m/49'/")
       -
            def get_client(self, force_pair=True):
                return self.plugin.get_client(self, force_pair)
        
       t@@ -79,7 +75,7 @@ class KeepKeyPlugin(HW_PluginBase):
            libraries_URL = 'https://github.com/keepkey/python-keepkey'
            minimum_firmware = (1, 0, 0)
            keystore_class = KeepKey_KeyStore
       -    SUPPORTED_XTYPES = ('standard', )
       +    SUPPORTED_XTYPES = ('standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh')
        
            MAX_LABEL_LEN = 32
        
       t@@ -232,6 +228,17 @@ class KeepKeyPlugin(HW_PluginBase):
                    client.load_device_by_xprv(item, pin, passphrase_protection,
                                               label, language)
        
       +    def _make_node_path(self, xpub, address_n):
       +        _, depth, fingerprint, child_num, chain_code, key = deserialize_xpub(xpub)
       +        node = self.types.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)
       +
            def setup_device(self, device_info, wizard, purpose):
                devmgr = self.device_manager()
                device_id = device_info.device.id_
       t@@ -256,12 +263,34 @@ class KeepKeyPlugin(HW_PluginBase):
                client.used()
                return xpub
        
       +    def get_keepkey_input_script_type(self, electrum_txin_type: str):
       +        if electrum_txin_type in ('p2wpkh', 'p2wsh'):
       +            return self.types.SPENDWITNESS
       +        if electrum_txin_type in ('p2wpkh-p2sh', 'p2wsh-p2sh'):
       +            return self.types.SPENDP2SHWITNESS
       +        if electrum_txin_type in ('p2pkh', ):
       +            return self.types.SPENDADDRESS
       +        if electrum_txin_type in ('p2sh', ):
       +            return self.types.SPENDMULTISIG
       +        raise ValueError('unexpected txin type: {}'.format(electrum_txin_type))
       +
       +    def get_keepkey_output_script_type(self, electrum_txin_type: str):
       +        if electrum_txin_type in ('p2wpkh', 'p2wsh'):
       +            return self.types.PAYTOWITNESS
       +        if electrum_txin_type in ('p2wpkh-p2sh', 'p2wsh-p2sh'):
       +            return self.types.PAYTOP2SHWITNESS
       +        if electrum_txin_type in ('p2pkh', ):
       +            return self.types.PAYTOADDRESS
       +        if electrum_txin_type in ('p2sh', ):
       +            return self.types.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
                client = self.get_client(keystore)
       -        inputs = self.tx_inputs(tx, True, keystore.is_segwit())
       -        outputs = self.tx_outputs(keystore.get_derivation(), tx, keystore.is_segwit())
       +        inputs = self.tx_inputs(tx, True)
       +        outputs = self.tx_outputs(keystore.get_derivation(), tx)
                signatures = client.sign_tx(self.get_coin_name(), inputs, outputs, lock_time=tx.locktime)[0]
                signatures = [(bh2u(x) + '01') for x in signatures]
                tx.update_signatures(signatures)
       t@@ -271,22 +300,34 @@ class KeepKeyPlugin(HW_PluginBase):
                    keystore = wallet.get_keystore()
                if not self.show_address_helper(wallet, address, keystore):
                    return
       -        if type(wallet) is not Standard_Wallet:
       -            keystore.handler.show_error(_('This function is only available for standard wallets when using {}.').format(self.device))
       -            return
       -        client = self.get_client(wallet.keystore)
       +        client = self.get_client(keystore)
                if not client.atleast_version(1, 3):
       -            wallet.keystore.handler.show_error(_("Your device firmware is too old"))
       +            keystore.handler.show_error(_("Your device firmware is too old"))
                    return
                change, index = wallet.get_address_index(address)
       -        derivation = wallet.keystore.derivation
       +        derivation = keystore.derivation
                address_path = "%s/%d/%d"%(derivation, change, index)
                address_n = client.expand_path(address_path)
       -        segwit = wallet.keystore.is_segwit()
       -        script_type = self.types.SPENDP2SHWITNESS if segwit else self.types.SPENDADDRESS
       -        client.get_address(self.get_coin_name(), address_n, True, script_type=script_type)
       -
       -    def tx_inputs(self, tx, for_sig=False, segwit=False):
       +        xpubs = wallet.get_master_public_keys()
       +        if len(xpubs) == 1:
       +            script_type = self.get_keepkey_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])
       +            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_keepkey_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):
                inputs = []
                for txin in tx.inputs():
                    txinputtype = self.types.TxInputType()
       t@@ -301,7 +342,7 @@ class KeepKeyPlugin(HW_PluginBase):
                                xpub, s = parse_xpubkey(x_pubkey)
                                xpub_n = self.client_class.expand_path(self.xpub_path[xpub])
                                txinputtype.address_n.extend(xpub_n + s)
       -                        txinputtype.script_type = self.types.SPENDP2SHWITNESS if segwit else self.types.SPENDADDRESS
       +                        txinputtype.script_type = self.get_keepkey_input_script_type(txin['type'])
                            else:
                                def f(x_pubkey):
                                    if is_xpubkey(x_pubkey):
       t@@ -309,15 +350,14 @@ class KeepKeyPlugin(HW_PluginBase):
                                    else:
                                        xpub = xpub_from_pubkey(0, bfh(x_pubkey))
                                        s = []
       -                            node = self.ckd_public.deserialize(xpub)
       -                            return self.types.HDNodePathType(node=node, address_n=s)
       -                        pubkeys = map(f, x_pubkeys)
       +                            return self._make_node_path(xpub, s)
       +                        pubkeys = list(map(f, x_pubkeys))
                                multisig = self.types.MultisigRedeemScriptType(
                                    pubkeys=pubkeys,
                                    signatures=map(lambda x: bfh(x)[:-1] if x else b'', txin.get('signatures')),
                                    m=txin.get('num_sig'),
                                )
       -                        script_type = self.types.SPENDP2SHWITNESS if segwit else self.types.SPENDMULTISIG
       +                        script_type = self.get_keepkey_input_script_type(txin['type'])
                                txinputtype = self.types.TxInputType(
                                    script_type=script_type,
                                    multisig=multisig
       t@@ -349,11 +389,11 @@ class KeepKeyPlugin(HW_PluginBase):
        
                return inputs
        
       -    def tx_outputs(self, derivation, tx, segwit=False):
       +    def tx_outputs(self, derivation, tx):
        
                def create_output_by_derivation():
       +            script_type = self.get_keepkey_output_script_type(info.script_type)
                    if len(xpubs) == 1:
       -                script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOADDRESS
                        address_n = self.client_class.expand_path(derivation + "/%d/%d" % index)
                        txoutputtype = self.types.TxOutputType(
                            amount=amount,
       t@@ -361,10 +401,8 @@ class KeepKeyPlugin(HW_PluginBase):
                            address_n=address_n,
                        )
                    else:
       -                script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOMULTISIG
                        address_n = self.client_class.expand_path("/%d/%d" % index)
       -                nodes = map(self.ckd_public.deserialize, xpubs)
       -                pubkeys = [self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
       +                pubkeys = [self._make_node_path(xpub, address_n) for xpub in xpubs]
                        multisig = self.types.MultisigRedeemScriptType(
                            pubkeys=pubkeys,
                            signatures=[b''] * len(pubkeys),
       t@@ -383,16 +421,7 @@ class KeepKeyPlugin(HW_PluginBase):
                        txoutputtype.script_type = self.types.PAYTOOPRETURN
                        txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(o)
                    elif _type == TYPE_ADDRESS:
       -                if is_segwit_address(address):
       -                    txoutputtype.script_type = self.types.PAYTOWITNESS
       -                else:
       -                    addrtype, hash_160 = b58_address_to_hash160(address)
       -                    if addrtype == constants.net.ADDRTYPE_P2PKH:
       -                        txoutputtype.script_type = self.types.PAYTOADDRESS
       -                    elif addrtype == constants.net.ADDRTYPE_P2SH:
       -                        txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
       -                    else:
       -                        raise Exception('addrtype: ' + str(addrtype))
       +                txoutputtype.script_type = self.types.PAYTOADDRESS
                        txoutputtype.address = address
                    return txoutputtype
        
       t@@ -424,6 +453,9 @@ class KeepKeyPlugin(HW_PluginBase):
        
            def electrum_tx_to_txtype(self, tx):
                t = self.types.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']
   DIR diff --git a/electrum/plugins/keepkey/qt.py b/electrum/plugins/keepkey/qt.py
       t@@ -198,13 +198,14 @@ class QtPlugin(QtPluginBase):
            @only_hook_if_libraries_available
            @hook
            def receive_menu(self, menu, addrs, wallet):
       -        if type(wallet) is not Standard_Wallet:
       +        if len(addrs) != 1:
                    return
       -        keystore = wallet.get_keystore()
       -        if type(keystore) == self.keystore_class and len(addrs) == 1:
       -            def show_address():
       -                keystore.thread.add(partial(self.show_address, wallet, addrs[0]))
       -            menu.addAction(_("Show on {}").format(self.device), show_address)
       +        for keystore in wallet.get_keystores():
       +            if type(keystore) == self.keystore_class:
       +                def show_address():
       +                    keystore.thread.add(partial(self.show_address, wallet, addrs[0], keystore))
       +                device_name = "{} ({})".format(self.device, keystore.label)
       +                menu.addAction(_("Show on {}").format(device_name), show_address)
        
            def show_settings_dialog(self, window, keystore):
                device_id = self.choose_device(window, keystore)
   DIR diff --git a/electrum/plugins/trezor/trezor.py b/electrum/plugins/trezor/trezor.py
       t@@ -3,8 +3,8 @@ import traceback
        import sys
        
        from electrum.util import bfh, bh2u, versiontuple, UserCancelled
       -from electrum.bitcoin import (b58_address_to_hash160, xpub_from_pubkey, deserialize_xpub,
       -                              TYPE_ADDRESS, TYPE_SCRIPT, is_address)
       +from electrum.bitcoin import (xpub_from_pubkey, deserialize_xpub,
       +                              TYPE_ADDRESS, TYPE_SCRIPT)
        from electrum import constants
        from electrum.i18n import _
        from electrum.plugin import BasePlugin, Device