URI: 
       tMerge pull request #6293 from btchip/ledger_segwit_trustedinputs - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 4aed1df0e8acd791fe2ac63491b140540be9dc57
   DIR parent 6d2aee18d0bc5b68bff4123855f2b77b7efa1f3b
  HTML Author: ghost43 <somber.night@protonmail.com>
       Date:   Thu,  2 Jul 2020 10:47:32 +0000
       
       Merge pull request #6293 from btchip/ledger_segwit_trustedinputs
       
       Ledger : Remove warning on Segwit inputs and newer Bitcoin application, use generic signing for P2SH inputs
       Diffstat:
         M contrib/requirements/requirements-… |       2 +-
         M electrum/plugins/ledger/ledger.py   |      59 ++++++++++++++++++++++---------
       
       2 files changed, 43 insertions(+), 18 deletions(-)
       ---
   DIR diff --git a/contrib/requirements/requirements-hw.txt b/contrib/requirements/requirements-hw.txt
       t@@ -11,7 +11,7 @@ Cython>=0.27
        trezor[hidapi]>=0.12.0
        safet>=0.1.5
        keepkey>=6.3.1
       -btchip-python>=0.1.26
       +btchip-python>=0.1.30
        ckcc-protocol>=0.7.7
        bitbox02>=4.0.0
        hidapi
   DIR diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py
       t@@ -18,7 +18,7 @@ from electrum.base_wizard import ScriptTypeNotSupported
        from electrum.logging import get_logger
        
        from ..hw_wallet import HW_PluginBase, HardwareClientBase
       -from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, validate_op_return_output
       +from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, validate_op_return_output, LibraryFoundButUnusable
        
        
        _logger = get_logger(__name__)
       t@@ -44,6 +44,7 @@ MSG_NEEDS_FW_UPDATE_SEGWIT = _('Firmware version (or "Bitcoin" app) too old for 
        MULTI_OUTPUT_SUPPORT = '1.1.4'
        SEGWIT_SUPPORT = '1.1.10'
        SEGWIT_SUPPORT_SPECIAL = '1.0.4'
       +SEGWIT_TRUSTEDINPUTS = '1.4.0'
        
        
        def test_pin_unlocked(func):
       t@@ -176,6 +177,9 @@ class Ledger_Client(HardwareClientBase):
            def supports_native_segwit(self):
                return self.nativeSegwitSupported
        
       +    def supports_segwit_trustedInputs(self):
       +        return self.segwitTrustedInputs
       +
            def perform_hw1_preflight(self):
                try:
                    firmwareInfo = self.dongleObject.getFirmwareVersion()
       t@@ -183,6 +187,7 @@ class Ledger_Client(HardwareClientBase):
                    self.multiOutputSupported = versiontuple(firmware) >= versiontuple(MULTI_OUTPUT_SUPPORT)
                    self.nativeSegwitSupported = versiontuple(firmware) >= versiontuple(SEGWIT_SUPPORT)
                    self.segwitSupported = self.nativeSegwitSupported or (firmwareInfo['specialVersion'] == 0x20 and versiontuple(firmware) >= versiontuple(SEGWIT_SUPPORT_SPECIAL))
       +            self.segwitTrustedInputs = versiontuple(firmware) >= versiontuple(SEGWIT_TRUSTEDINPUTS)
        
                    if not checkFirmware(firmwareInfo):
                        self.close()
       t@@ -346,7 +351,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
                p2shTransaction = False
                segwitTransaction = False
                pin = ""
       -        self.get_client() # prompt for the PIN before displaying the dialog if necessary
       +        client_ledger = self.get_client() # prompt for the PIN before displaying the dialog if necessary
                client_electrum = self.get_client_electrum()
                assert client_electrum
        
       t@@ -437,18 +442,23 @@ class Ledger_KeyStore(Hardware_KeyStore):
                    # Get trusted inputs from the original transactions
                    for utxo in inputs:
                        sequence = int_to_hex(utxo[5], 4)
       -                if segwitTransaction:
       +                if segwitTransaction and not client_electrum.supports_segwit_trustedInputs():
                            tmp = bfh(utxo[3])[::-1]
                            tmp += bfh(int_to_hex(utxo[1], 4))
                            tmp += bfh(int_to_hex(utxo[6], 8))  # txin['value']
                            chipInputs.append({'value' : tmp, 'witness' : True, 'sequence' : sequence})
                            redeemScripts.append(bfh(utxo[2]))
       -                elif not p2shTransaction:
       +                elif (not p2shTransaction) or client_electrum.supports_multi_output():
                            txtmp = bitcoinTransaction(bfh(utxo[0]))
       -                    trustedInput = self.get_client().getTrustedInput(txtmp, utxo[1])
       +                    trustedInput = client_ledger.getTrustedInput(txtmp, utxo[1])
                            trustedInput['sequence'] = sequence
       +                    if segwitTransaction:
       +                        trustedInput['witness'] = True
                            chipInputs.append(trustedInput)
       -                    redeemScripts.append(txtmp.outputs[utxo[1]].script)
       +                    if p2shTransaction or segwitTransaction:
       +                        redeemScripts.append(bfh(utxo[2]))
       +                    else:
       +                        redeemScripts.append(txtmp.outputs[utxo[1]].script)
                        else:
                            tmp = bfh(utxo[3])[::-1]
                            tmp += bfh(int_to_hex(utxo[1], 4))
       t@@ -459,13 +469,13 @@ class Ledger_KeyStore(Hardware_KeyStore):
                    firstTransaction = True
                    inputIndex = 0
                    rawTx = tx.serialize_to_network()
       -            self.get_client().enableAlternate2fa(False)
       +            client_ledger.enableAlternate2fa(False)
                    if segwitTransaction:
       -                self.get_client().startUntrustedTransaction(True, inputIndex,
       +                client_ledger.startUntrustedTransaction(True, inputIndex,
                                                                    chipInputs, redeemScripts[inputIndex], version=tx.version)
                        # we don't set meaningful outputAddress, amount and fees
                        # as we only care about the alternateEncoding==True branch
       -                outputData = self.get_client().finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
       +                outputData = client_ledger.finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
                        outputData['outputData'] = txOutput
                        if outputData['confirmationNeeded']:
                            outputData['address'] = output
       t@@ -476,9 +486,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
                            self.handler.show_message(_("Confirmed. Signing Transaction..."))
                        while inputIndex < len(inputs):
                            singleInput = [ chipInputs[inputIndex] ]
       -                    self.get_client().startUntrustedTransaction(False, 0,
       +                    client_ledger.startUntrustedTransaction(False, 0,
                                                                    singleInput, redeemScripts[inputIndex], version=tx.version)
       -                    inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin, lockTime=tx.locktime)
       +                    inputSignature = client_ledger.untrustedHashSign(inputsPaths[inputIndex], pin, lockTime=tx.locktime)
                            inputSignature[0] = 0x30 # force for 1.4.9+
                            my_pubkey = inputs[inputIndex][4]
                            tx.add_signature_to_txin(txin_idx=inputIndex,
       t@@ -487,11 +497,11 @@ class Ledger_KeyStore(Hardware_KeyStore):
                            inputIndex = inputIndex + 1
                    else:
                        while inputIndex < len(inputs):
       -                    self.get_client().startUntrustedTransaction(firstTransaction, inputIndex,
       +                    client_ledger.startUntrustedTransaction(firstTransaction, inputIndex,
                                                                        chipInputs, redeemScripts[inputIndex], version=tx.version)
                            # we don't set meaningful outputAddress, amount and fees
                            # as we only care about the alternateEncoding==True branch
       -                    outputData = self.get_client().finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
       +                    outputData = client_ledger.finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
                            outputData['outputData'] = txOutput
                            if outputData['confirmationNeeded']:
                                outputData['address'] = output
       t@@ -502,7 +512,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
                                self.handler.show_message(_("Confirmed. Signing Transaction..."))
                            else:
                                # Sign input with the provided PIN
       -                        inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin, lockTime=tx.locktime)
       +                        inputSignature = client_ledger.untrustedHashSign(inputsPaths[inputIndex], pin, lockTime=tx.locktime)
                                inputSignature[0] = 0x30 # force for 1.4.9+
                                my_pubkey = inputs[inputIndex][4]
                                tx.add_signature_to_txin(txin_idx=inputIndex,
       t@@ -557,8 +567,8 @@ class Ledger_KeyStore(Hardware_KeyStore):
                    self.handler.finished()
        
        class LedgerPlugin(HW_PluginBase):
       -    libraries_available = BTCHIP
            keystore_class = Ledger_KeyStore
       +    minimum_library = (0, 1, 30)
            client = None
            DEVICE_IDS = [
                           (0x2581, 0x1807), # HW.1 legacy btchip
       t@@ -580,8 +590,23 @@ class LedgerPlugin(HW_PluginBase):
            def __init__(self, parent, config, name):
                self.segwit = config.get("segwit")
                HW_PluginBase.__init__(self, parent, config, name)
       -        if self.libraries_available:
       -            self.device_manager().register_devices(self.DEVICE_IDS, plugin=self)
       +        self.libraries_available = self.check_libraries_available()
       +        if not self.libraries_available:
       +            return
       +        self.device_manager().register_devices(self.DEVICE_IDS, plugin=self)
       +
       +    def get_library_version(self):
       +        try:
       +            import btchip
       +            version = btchip.__version__
       +        except ImportError:
       +            raise
       +        except:
       +            version = "unknown"
       +        if BTCHIP:
       +            return version
       +        else:
       +            raise LibraryFoundButUnusable(library_version=version)
        
            def get_btchip_device(self, device):
                ledger = False