electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 4aed1df0e8acd791fe2ac63491b140540be9dc57
parent 6d2aee18d0bc5b68bff4123855f2b77b7efa1f3b
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:
Mcontrib/requirements/requirements-hw.txt | 2+-
Melectrum/plugins/ledger/ledger.py | 59++++++++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/contrib/requirements/requirements-hw.txt b/contrib/requirements/requirements-hw.txt @@ -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 diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py @@ -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__) @@ -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): @@ -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() @@ -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() @@ -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 @@ -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)) @@ -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 @@ -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, @@ -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 @@ -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, @@ -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 @@ -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