mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-27 07:23:25 +00:00
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
This commit is contained in:
commit
4aed1df0e8
2 changed files with 43 additions and 18 deletions
|
@ -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
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue