Browse Source

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
bip39-recovery
ghost43 5 years ago
committed by GitHub
parent
commit
4aed1df0e8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      contrib/requirements/requirements-hw.txt
  2. 59
      electrum/plugins/ledger/ledger.py

2
contrib/requirements/requirements-hw.txt

@ -11,7 +11,7 @@ Cython>=0.27
trezor[hidapi]>=0.12.0 trezor[hidapi]>=0.12.0
safet>=0.1.5 safet>=0.1.5
keepkey>=6.3.1 keepkey>=6.3.1
btchip-python>=0.1.26 btchip-python>=0.1.30
ckcc-protocol>=0.7.7 ckcc-protocol>=0.7.7
bitbox02>=4.0.0 bitbox02>=4.0.0
hidapi hidapi

59
electrum/plugins/ledger/ledger.py

@ -18,7 +18,7 @@ from electrum.base_wizard import ScriptTypeNotSupported
from electrum.logging import get_logger from electrum.logging import get_logger
from ..hw_wallet import HW_PluginBase, HardwareClientBase 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__) _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' MULTI_OUTPUT_SUPPORT = '1.1.4'
SEGWIT_SUPPORT = '1.1.10' SEGWIT_SUPPORT = '1.1.10'
SEGWIT_SUPPORT_SPECIAL = '1.0.4' SEGWIT_SUPPORT_SPECIAL = '1.0.4'
SEGWIT_TRUSTEDINPUTS = '1.4.0'
def test_pin_unlocked(func): def test_pin_unlocked(func):
@ -176,6 +177,9 @@ class Ledger_Client(HardwareClientBase):
def supports_native_segwit(self): def supports_native_segwit(self):
return self.nativeSegwitSupported return self.nativeSegwitSupported
def supports_segwit_trustedInputs(self):
return self.segwitTrustedInputs
def perform_hw1_preflight(self): def perform_hw1_preflight(self):
try: try:
firmwareInfo = self.dongleObject.getFirmwareVersion() firmwareInfo = self.dongleObject.getFirmwareVersion()
@ -183,6 +187,7 @@ class Ledger_Client(HardwareClientBase):
self.multiOutputSupported = versiontuple(firmware) >= versiontuple(MULTI_OUTPUT_SUPPORT) self.multiOutputSupported = versiontuple(firmware) >= versiontuple(MULTI_OUTPUT_SUPPORT)
self.nativeSegwitSupported = versiontuple(firmware) >= versiontuple(SEGWIT_SUPPORT) self.nativeSegwitSupported = versiontuple(firmware) >= versiontuple(SEGWIT_SUPPORT)
self.segwitSupported = self.nativeSegwitSupported or (firmwareInfo['specialVersion'] == 0x20 and versiontuple(firmware) >= versiontuple(SEGWIT_SUPPORT_SPECIAL)) 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): if not checkFirmware(firmwareInfo):
self.close() self.close()
@ -346,7 +351,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
p2shTransaction = False p2shTransaction = False
segwitTransaction = False segwitTransaction = False
pin = "" 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() client_electrum = self.get_client_electrum()
assert client_electrum assert client_electrum
@ -437,18 +442,23 @@ class Ledger_KeyStore(Hardware_KeyStore):
# Get trusted inputs from the original transactions # Get trusted inputs from the original transactions
for utxo in inputs: for utxo in inputs:
sequence = int_to_hex(utxo[5], 4) 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(utxo[3])[::-1]
tmp += bfh(int_to_hex(utxo[1], 4)) tmp += bfh(int_to_hex(utxo[1], 4))
tmp += bfh(int_to_hex(utxo[6], 8)) # txin['value'] tmp += bfh(int_to_hex(utxo[6], 8)) # txin['value']
chipInputs.append({'value' : tmp, 'witness' : True, 'sequence' : sequence}) chipInputs.append({'value' : tmp, 'witness' : True, 'sequence' : sequence})
redeemScripts.append(bfh(utxo[2])) redeemScripts.append(bfh(utxo[2]))
elif not p2shTransaction: elif (not p2shTransaction) or client_electrum.supports_multi_output():
txtmp = bitcoinTransaction(bfh(utxo[0])) txtmp = bitcoinTransaction(bfh(utxo[0]))
trustedInput = self.get_client().getTrustedInput(txtmp, utxo[1]) trustedInput = client_ledger.getTrustedInput(txtmp, utxo[1])
trustedInput['sequence'] = sequence trustedInput['sequence'] = sequence
if segwitTransaction:
trustedInput['witness'] = True
chipInputs.append(trustedInput) 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: else:
tmp = bfh(utxo[3])[::-1] tmp = bfh(utxo[3])[::-1]
tmp += bfh(int_to_hex(utxo[1], 4)) tmp += bfh(int_to_hex(utxo[1], 4))
@ -459,13 +469,13 @@ class Ledger_KeyStore(Hardware_KeyStore):
firstTransaction = True firstTransaction = True
inputIndex = 0 inputIndex = 0
rawTx = tx.serialize_to_network() rawTx = tx.serialize_to_network()
self.get_client().enableAlternate2fa(False) client_ledger.enableAlternate2fa(False)
if segwitTransaction: if segwitTransaction:
self.get_client().startUntrustedTransaction(True, inputIndex, client_ledger.startUntrustedTransaction(True, inputIndex,
chipInputs, redeemScripts[inputIndex], version=tx.version) chipInputs, redeemScripts[inputIndex], version=tx.version)
# we don't set meaningful outputAddress, amount and fees # we don't set meaningful outputAddress, amount and fees
# as we only care about the alternateEncoding==True branch # 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 outputData['outputData'] = txOutput
if outputData['confirmationNeeded']: if outputData['confirmationNeeded']:
outputData['address'] = output outputData['address'] = output
@ -476,9 +486,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
self.handler.show_message(_("Confirmed. Signing Transaction...")) self.handler.show_message(_("Confirmed. Signing Transaction..."))
while inputIndex < len(inputs): while inputIndex < len(inputs):
singleInput = [ chipInputs[inputIndex] ] singleInput = [ chipInputs[inputIndex] ]
self.get_client().startUntrustedTransaction(False, 0, client_ledger.startUntrustedTransaction(False, 0,
singleInput, redeemScripts[inputIndex], version=tx.version) 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+ inputSignature[0] = 0x30 # force for 1.4.9+
my_pubkey = inputs[inputIndex][4] my_pubkey = inputs[inputIndex][4]
tx.add_signature_to_txin(txin_idx=inputIndex, tx.add_signature_to_txin(txin_idx=inputIndex,
@ -487,11 +497,11 @@ class Ledger_KeyStore(Hardware_KeyStore):
inputIndex = inputIndex + 1 inputIndex = inputIndex + 1
else: else:
while inputIndex < len(inputs): while inputIndex < len(inputs):
self.get_client().startUntrustedTransaction(firstTransaction, inputIndex, client_ledger.startUntrustedTransaction(firstTransaction, inputIndex,
chipInputs, redeemScripts[inputIndex], version=tx.version) chipInputs, redeemScripts[inputIndex], version=tx.version)
# we don't set meaningful outputAddress, amount and fees # we don't set meaningful outputAddress, amount and fees
# as we only care about the alternateEncoding==True branch # 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 outputData['outputData'] = txOutput
if outputData['confirmationNeeded']: if outputData['confirmationNeeded']:
outputData['address'] = output outputData['address'] = output
@ -502,7 +512,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
self.handler.show_message(_("Confirmed. Signing Transaction...")) self.handler.show_message(_("Confirmed. Signing Transaction..."))
else: else:
# Sign input with the provided PIN # 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+ inputSignature[0] = 0x30 # force for 1.4.9+
my_pubkey = inputs[inputIndex][4] my_pubkey = inputs[inputIndex][4]
tx.add_signature_to_txin(txin_idx=inputIndex, tx.add_signature_to_txin(txin_idx=inputIndex,
@ -557,8 +567,8 @@ class Ledger_KeyStore(Hardware_KeyStore):
self.handler.finished() self.handler.finished()
class LedgerPlugin(HW_PluginBase): class LedgerPlugin(HW_PluginBase):
libraries_available = BTCHIP
keystore_class = Ledger_KeyStore keystore_class = Ledger_KeyStore
minimum_library = (0, 1, 30)
client = None client = None
DEVICE_IDS = [ DEVICE_IDS = [
(0x2581, 0x1807), # HW.1 legacy btchip (0x2581, 0x1807), # HW.1 legacy btchip
@ -580,8 +590,23 @@ class LedgerPlugin(HW_PluginBase):
def __init__(self, parent, config, name): def __init__(self, parent, config, name):
self.segwit = config.get("segwit") self.segwit = config.get("segwit")
HW_PluginBase.__init__(self, parent, config, name) HW_PluginBase.__init__(self, parent, config, name)
if self.libraries_available: self.libraries_available = self.check_libraries_available()
self.device_manager().register_devices(self.DEVICE_IDS, plugin=self) 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): def get_btchip_device(self, device):
ledger = False ledger = False

Loading…
Cancel
Save