Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ledger : Remove warning on Segwit inputs and newer Bitcoin application, use ge… #6293

Merged
merged 3 commits into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contrib/requirements/requirements-hw.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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>=3.0.0
hidapi
59 changes: 42 additions & 17 deletions electrum/plugins/ledger/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
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__)
Expand All @@ -44,6 +44,7 @@
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):
Expand Down Expand Up @@ -176,13 +177,17 @@ def supports_segwit(self):
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()
firmware = firmwareInfo['version']
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()
Expand Down Expand Up @@ -346,7 +351,7 @@ def sign_transaction(self, tx, password):
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

Expand Down Expand Up @@ -437,18 +442,23 @@ def sign_transaction(self, tx, password):
# 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))
Expand All @@ -459,13 +469,13 @@ def sign_transaction(self, tx, password):
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
Expand All @@ -476,9 +486,9 @@ def sign_transaction(self, tx, password):
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,
Expand All @@ -487,11 +497,11 @@ def sign_transaction(self, tx, password):
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
Expand All @@ -502,7 +512,7 @@ def sign_transaction(self, tx, password):
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,
Expand Down Expand Up @@ -557,8 +567,8 @@ def show_address(self, sequence, txin_type):
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
Expand All @@ -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
Expand Down