plugins: ledger: rm support for hw.1
This removes support for Ledger HW.1 and "Nano" (non-S) devices.
These were manufactured/sold around 2015-2016, and are long unsupported by the upstream vendor.
We previously added a deprecation warning to the GUI [0] released in 4.3.3 (2023-01-02), to warn owners of these devices.
This PR now fully removes support.
As a consequence, the unmaintained btchip-python dependency can now be removed, which solves [1].
[0]: 9b82eb6d06
[1]: https://github.com/spesmilo/electrum/issues/9370#issuecomment-2593675364
This commit is contained in:
@@ -41,7 +41,6 @@ datas = [
|
||||
datas += collect_data_files(f"{PYPKG}.plugins")
|
||||
datas += collect_data_files('trezorlib') # TODO is this needed? and same question for other hww libs
|
||||
datas += collect_data_files('safetlib')
|
||||
datas += collect_data_files('btchip')
|
||||
datas += collect_data_files('ckcc')
|
||||
datas += collect_data_files('bitbox02')
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ base58==2.1.1 \
|
||||
bitbox02==6.2.0 \
|
||||
--hash=sha256:5a8290bd270468ccdf2e6ff7174d25ea2b2f191e19734a79aa573c2b982c266f \
|
||||
--hash=sha256:cede06e399c98ed536fed6d8a421208daa00f97b697bd8363a941ac5f33309bf
|
||||
btchip-python==0.1.32 \
|
||||
--hash=sha256:34f5e0c161c08f65dc0d070ba2ff4c315ed21c4b7e0faa32a46862d0dc1b8f55
|
||||
cbor2==5.4.6 \
|
||||
--hash=sha256:b893500db0fe033e570c3adc956af6eefc57e280026bd2d86fd53da9f1e594d7
|
||||
certifi==2024.2.2 \
|
||||
|
||||
@@ -44,7 +44,6 @@ datas = [
|
||||
datas += collect_data_files(f"{PYPKG}.plugins")
|
||||
datas += collect_data_files('trezorlib') # TODO is this needed? and same question for other hww libs
|
||||
datas += collect_data_files('safetlib')
|
||||
datas += collect_data_files('btchip')
|
||||
datas += collect_data_files('ckcc')
|
||||
datas += collect_data_files('bitbox02')
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ hidapi>=0.7.99.post15
|
||||
libusb1>=1.6
|
||||
|
||||
# device plugin: ledger
|
||||
# note: btchip-python only needed for "legacy" protocol and HW.1 support
|
||||
btchip-python>=0.1.32
|
||||
ledger-bitcoin>=0.2.0
|
||||
hidapi
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
import copy
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt6.QtWidgets import (QDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel,
|
||||
QWidget, QHBoxLayout, QComboBox)
|
||||
|
||||
from btchip.btchip import BTChipException
|
||||
|
||||
from electrum.gui.qt.util import PasswordLineEdit
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum import constants, bitcoin
|
||||
from electrum.logging import get_logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .ledger import Ledger_Client
|
||||
|
||||
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
||||
helpTxt = [_("Your Ledger Wallet wants to tell you a one-time PIN code.<br><br>" \
|
||||
"For best security you should unplug your device, open a text editor on another computer, " \
|
||||
"put your cursor into it, and plug your device into that computer. " \
|
||||
"It will output a summary of the transaction being signed and a one-time PIN.<br><br>" \
|
||||
"Verify the transaction summary and type the PIN code here.<br><br>" \
|
||||
"Before pressing enter, plug the device back into this computer.<br>"),
|
||||
_("Verify the address below.<br>Type the character from your security card corresponding to the <u><b>BOLD</b></u> character."),
|
||||
]
|
||||
|
||||
class LedgerAuthDialog(QDialog):
|
||||
def __init__(self, handler, data, *, client: 'Ledger_Client'):
|
||||
'''Ask user for 2nd factor authentication. Support text and security card methods.
|
||||
Use last method from settings, but support downgrade.
|
||||
'''
|
||||
QDialog.__init__(self, handler.top_level_window())
|
||||
self.handler = handler
|
||||
self.txdata = data
|
||||
self.idxs = self.txdata['keycardData'] if self.txdata['confirmationType'] > 1 else ''
|
||||
self.setMinimumWidth(650)
|
||||
self.setWindowTitle(_("Ledger Wallet Authentication"))
|
||||
self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
|
||||
self.dongle = client.dongleObject.dongle
|
||||
self.pin = ''
|
||||
|
||||
self.devmode = self.getDevice2FAMode()
|
||||
if self.devmode == 0x11 or self.txdata['confirmationType'] == 1:
|
||||
self.cfg['mode'] = 0
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
self.setLayout(vbox)
|
||||
|
||||
def on_change_mode(idx):
|
||||
self.cfg['mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1
|
||||
if self.cfg['mode'] > 0:
|
||||
self.handler.win.wallet.get_keystore().cfg = self.cfg
|
||||
self.handler.win.wallet.save_keystore()
|
||||
self.update_dlg()
|
||||
def return_pin():
|
||||
self.pin = self.pintxt.text() if self.txdata['confirmationType'] == 1 else self.cardtxt.text()
|
||||
if self.cfg['mode'] == 1:
|
||||
self.pin = ''.join(chr(int(str(i),16)) for i in self.pin)
|
||||
self.accept()
|
||||
|
||||
self.modebox = QWidget()
|
||||
modelayout = QHBoxLayout()
|
||||
self.modebox.setLayout(modelayout)
|
||||
modelayout.addWidget(QLabel(_("Method:")))
|
||||
self.modes = QComboBox()
|
||||
modelayout.addWidget(self.modes, 2)
|
||||
modelayout.addStretch(1)
|
||||
self.modebox.setMaximumHeight(50)
|
||||
vbox.addWidget(self.modebox)
|
||||
|
||||
self.populate_modes()
|
||||
self.modes.currentIndexChanged.connect(on_change_mode)
|
||||
|
||||
self.helpmsg = QTextEdit()
|
||||
self.helpmsg.setStyleSheet("QTextEdit { color:black; background-color: lightgray; }")
|
||||
self.helpmsg.setReadOnly(True)
|
||||
vbox.addWidget(self.helpmsg)
|
||||
|
||||
self.pinbox = QWidget()
|
||||
pinlayout = QHBoxLayout()
|
||||
self.pinbox.setLayout(pinlayout)
|
||||
self.pintxt = PasswordLineEdit()
|
||||
self.pintxt.setMaxLength(4)
|
||||
self.pintxt.returnPressed.connect(return_pin)
|
||||
pinlayout.addWidget(QLabel(_("Enter PIN:")))
|
||||
pinlayout.addWidget(self.pintxt)
|
||||
pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
|
||||
pinlayout.addStretch(1)
|
||||
self.pinbox.setVisible(self.cfg['mode'] == 0)
|
||||
vbox.addWidget(self.pinbox)
|
||||
|
||||
self.cardbox = QWidget()
|
||||
card = QVBoxLayout()
|
||||
self.cardbox.setLayout(card)
|
||||
self.addrtext = QTextEdit()
|
||||
self.addrtext.setStyleSheet('''
|
||||
QTextEdit {
|
||||
color:blue; background-color:lightgray; padding:15px 10px; border:none;
|
||||
font-size:20pt; font-family: "Courier New", monospace; }
|
||||
''')
|
||||
self.addrtext.setReadOnly(True)
|
||||
self.addrtext.setMaximumHeight(130)
|
||||
card.addWidget(self.addrtext)
|
||||
|
||||
def pin_changed(s):
|
||||
if len(s) < len(self.idxs):
|
||||
i = self.idxs[len(s)]
|
||||
addr = self.txdata['address']
|
||||
if not constants.net.TESTNET:
|
||||
text = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:]
|
||||
else:
|
||||
# pin needs to be created from mainnet address
|
||||
addr_mainnet = bitcoin.script_to_address(bitcoin.address_to_script(addr), net=constants.BitcoinMainnet)
|
||||
addr_mainnet = addr_mainnet[:i] + '<u><b>' + addr_mainnet[i:i+1] + '</u></b>' + addr_mainnet[i+1:]
|
||||
text = str(addr) + '\n' + str(addr_mainnet)
|
||||
self.addrtext.setHtml(str(text))
|
||||
else:
|
||||
self.addrtext.setHtml(_("Press Enter"))
|
||||
|
||||
pin_changed('')
|
||||
cardpin = QHBoxLayout()
|
||||
cardpin.addWidget(QLabel(_("Enter PIN:")))
|
||||
self.cardtxt = PasswordLineEdit()
|
||||
self.cardtxt.setMaxLength(len(self.idxs))
|
||||
self.cardtxt.textChanged.connect(pin_changed)
|
||||
self.cardtxt.returnPressed.connect(return_pin)
|
||||
cardpin.addWidget(self.cardtxt)
|
||||
cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
|
||||
cardpin.addStretch(1)
|
||||
card.addLayout(cardpin)
|
||||
self.cardbox.setVisible(self.cfg['mode'] == 1)
|
||||
vbox.addWidget(self.cardbox)
|
||||
|
||||
self.update_dlg()
|
||||
|
||||
def populate_modes(self):
|
||||
self.modes.blockSignals(True)
|
||||
self.modes.clear()
|
||||
self.modes.addItem(_("Summary Text PIN (requires dongle replugging)") if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled"))
|
||||
if self.txdata['confirmationType'] > 1:
|
||||
self.modes.addItem(_("Security Card Challenge"))
|
||||
self.modes.blockSignals(False)
|
||||
|
||||
def update_dlg(self):
|
||||
self.modes.setCurrentIndex(self.cfg['mode'])
|
||||
self.modebox.setVisible(True)
|
||||
self.helpmsg.setText(helpTxt[self.cfg['mode']])
|
||||
self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100)
|
||||
self.helpmsg.setVisible(True)
|
||||
self.pinbox.setVisible(self.cfg['mode'] == 0)
|
||||
self.cardbox.setVisible(self.cfg['mode'] == 1)
|
||||
self.pintxt.setFocus() if self.cfg['mode'] == 0 else self.cardtxt.setFocus()
|
||||
self.setMaximumHeight(400)
|
||||
|
||||
def getDevice2FAMode(self):
|
||||
apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode
|
||||
try:
|
||||
mode = self.dongle.exchange(bytearray(apdu))
|
||||
return mode
|
||||
except BTChipException as e:
|
||||
_logger.debug('Device getMode Failed')
|
||||
return 0x11
|
||||
@@ -39,13 +39,12 @@ try:
|
||||
from ledgercomm.interfaces.hid_device import HID
|
||||
|
||||
# legacy imports
|
||||
# note: we could replace "btchip" with "ledger_bitcoin.btchip" but the latter does not support HW.1
|
||||
import hid
|
||||
from btchip.btchipComm import HIDDongleHIDAPI
|
||||
from btchip.btchip import btchip
|
||||
from btchip.btchipUtils import compress_public_key
|
||||
from btchip.bitcoinTransaction import bitcoinTransaction
|
||||
from btchip.btchipException import BTChipException
|
||||
from ledger_bitcoin.btchip.btchipComm import HIDDongleHIDAPI
|
||||
from ledger_bitcoin.btchip.btchip import btchip
|
||||
from ledger_bitcoin.btchip.btchipUtils import compress_public_key
|
||||
from ledger_bitcoin.btchip.bitcoinTransaction import bitcoinTransaction
|
||||
from ledger_bitcoin.btchip.btchipException import BTChipException
|
||||
|
||||
LEDGER_BITCOIN = True
|
||||
except ImportError as e:
|
||||
@@ -310,7 +309,7 @@ class Ledger_Client(HardwareClientBase, ABC):
|
||||
def construct_new(*args, device: Device, **kwargs) -> 'Ledger_Client':
|
||||
"""The 'real' constructor, that automatically decides which subclass to use."""
|
||||
if LedgerPlugin.is_hw1(device.product_key):
|
||||
return Ledger_Client_Legacy_HW1(*args, **kwargs, device=device)
|
||||
raise Exception("ledger hw.1 devices are no longer supported")
|
||||
# for nano S or newer hw, decide which client impl to use based on software/firmware version:
|
||||
hid_device = HID()
|
||||
hid_device.path = device.path
|
||||
@@ -328,8 +327,10 @@ class Ledger_Client(HardwareClientBase, ABC):
|
||||
_logger.info(f"ledger_bitcoin.createClient() got exc: {e}. falling back to old plugin.")
|
||||
cl = None
|
||||
if isinstance(cl, ledger_bitcoin.client.NewClient):
|
||||
_logger.debug(f"Ledger_Client.construct_new(). creating NewClient for {device=}.")
|
||||
return Ledger_Client_New(hid_device, *args, **kwargs)
|
||||
else:
|
||||
_logger.debug(f"Ledger_Client.construct_new(). creating LegacyClient for {device=}.")
|
||||
return Ledger_Client_Legacy(hid_device, *args, **kwargs)
|
||||
|
||||
def __init__(self, *, plugin: HW_PluginBase):
|
||||
@@ -552,7 +553,6 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
chipInputs = []
|
||||
redeemScripts = []
|
||||
changePath = ""
|
||||
output = None
|
||||
p2shTransaction = False
|
||||
segwitTransaction = False
|
||||
pin = ""
|
||||
@@ -606,30 +606,16 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
if not is_txin_legacy_multisig(txin):
|
||||
self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen
|
||||
|
||||
txOutput = bytearray()
|
||||
txOutput += var_int(len(tx.outputs()))
|
||||
for o in tx.outputs():
|
||||
txOutput += int.to_bytes(o.value, length=8, byteorder="little", signed=False)
|
||||
script = o.scriptpubkey
|
||||
txOutput += var_int(len(script))
|
||||
txOutput += script
|
||||
txOutput = bytes(txOutput)
|
||||
|
||||
if not self.supports_multi_output():
|
||||
if len(tx.outputs()) > 2:
|
||||
self.give_error("Transaction with more than 2 outputs not supported")
|
||||
for txout in tx.outputs():
|
||||
if self.is_hw1() and txout.address and not is_b58_address(txout.address):
|
||||
self.give_error(_("This {} device can only send to base58 addresses.").format(keystore.device))
|
||||
if not txout.address:
|
||||
if self.is_hw1():
|
||||
self.give_error(_("Only address outputs are supported by {}").format(keystore.device))
|
||||
# note: max_size based on https://github.com/LedgerHQ/ledger-app-btc/commit/3a78dee9c0484821df58975803e40d58fbfc2c38#diff-c61ccd96a6d8b54d48f54a3bc4dfa7e2R26
|
||||
validate_op_return_output(txout, max_size=190)
|
||||
|
||||
# Output "change" detection
|
||||
# - only one output and one change is authorized (for hw.1 and nano)
|
||||
# - at most one output can bypass confirmation (~change) (for all)
|
||||
# - at most one output can bypass confirmation (~change)
|
||||
if not p2shTransaction:
|
||||
has_change = False
|
||||
any_output_on_change_branch = is_any_tx_output_on_change_branch(tx)
|
||||
@@ -643,10 +629,6 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
assert changePath
|
||||
changePath = convert_bip32_intpath_to_strpath(changePath)[2:]
|
||||
has_change = True
|
||||
else:
|
||||
output = txout.address
|
||||
else:
|
||||
output = txout.address
|
||||
|
||||
try:
|
||||
# Get trusted inputs from the original transactions
|
||||
@@ -681,22 +663,11 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
firstTransaction = True
|
||||
inputIndex = 0
|
||||
rawTx = tx.serialize_to_network(include_sigs=False)
|
||||
if self.is_hw1():
|
||||
self.dongleObject.enableAlternate2fa(False)
|
||||
if segwitTransaction:
|
||||
self.dongleObject.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.dongleObject.finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
|
||||
outputData['outputData'] = txOutput
|
||||
if outputData['confirmationNeeded']:
|
||||
outputData['address'] = output
|
||||
self.handler.finished()
|
||||
# do the authenticate dialog and get pin:
|
||||
pin = self.handler.get_auth(outputData, client=self)
|
||||
if not pin:
|
||||
raise UserWarning()
|
||||
self.handler.show_message(_("Confirmed. Signing Transaction..."))
|
||||
while inputIndex < len(inputs):
|
||||
self.handler.show_message(_("Signing transaction...") + f" (phase2, {inputIndex}/{len(inputs)})")
|
||||
singleInput = [chipInputs[inputIndex]]
|
||||
@@ -716,24 +687,14 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
# we don't set meaningful outputAddress, amount and fees
|
||||
# as we only care about the alternateEncoding==True branch
|
||||
outputData = self.dongleObject.finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
|
||||
outputData['outputData'] = txOutput
|
||||
if outputData['confirmationNeeded']:
|
||||
outputData['address'] = output
|
||||
self.handler.finished()
|
||||
# do the authenticate dialog and get pin:
|
||||
pin = self.handler.get_auth(outputData, client=self)
|
||||
if not pin:
|
||||
raise UserWarning()
|
||||
self.handler.show_message(_("Confirmed. Signing Transaction..."))
|
||||
else:
|
||||
# Sign input with the provided PIN
|
||||
inputSignature = self.dongleObject.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,
|
||||
signing_pubkey=my_pubkey,
|
||||
sig=inputSignature)
|
||||
inputIndex = inputIndex + 1
|
||||
# Sign input with the provided PIN
|
||||
inputSignature = self.dongleObject.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,
|
||||
signing_pubkey=my_pubkey,
|
||||
sig=inputSignature)
|
||||
inputIndex = inputIndex + 1
|
||||
firstTransaction = False
|
||||
except UserWarning:
|
||||
self.handler.show_error(_('Cancelled by user'))
|
||||
@@ -770,12 +731,6 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
try:
|
||||
info = self.dongleObject.signMessagePrepare(address_path, message)
|
||||
pin = ""
|
||||
if info['confirmationNeeded']:
|
||||
# do the authenticate dialog and get pin:
|
||||
pin = self.handler.get_auth(info, client=self)
|
||||
if not pin:
|
||||
raise UserWarning(_('Cancelled by user'))
|
||||
pin = str(pin).encode()
|
||||
signature = self.dongleObject.signMessageSign(pin)
|
||||
except BTChipException as e:
|
||||
if e.sw == 0x6a80:
|
||||
@@ -813,88 +768,6 @@ class Ledger_Client_Legacy(Ledger_Client):
|
||||
return bytes([27 + 4 + (signature[0] & 0x01)]) + r_padded + s_padded
|
||||
|
||||
|
||||
class Ledger_Client_Legacy_HW1(Ledger_Client_Legacy):
|
||||
"""Even "legacy-er" client for deprecated HW.1 support."""
|
||||
|
||||
MIN_SUPPORTED_HW1_FW_VERSION = "1.0.2"
|
||||
|
||||
def __init__(self, product_key: Tuple[int, int],
|
||||
plugin: HW_PluginBase, device: 'Device'):
|
||||
# note: Ledger_Client_Legacy.__init__ is *not* called
|
||||
Ledger_Client.__init__(self, plugin=plugin)
|
||||
self._product_key = product_key
|
||||
assert self.is_hw1()
|
||||
|
||||
ledger = device.product_key[1] in (0x3b7c, 0x4b7c)
|
||||
dev = hid.device()
|
||||
dev.open_path(device.path)
|
||||
dev.set_nonblocking(True)
|
||||
hid_device = HIDDongleHIDAPI(dev, ledger, debug=False)
|
||||
self.dongleObject = btchip(hid_device)
|
||||
|
||||
self._preflightDone = False
|
||||
self.signing = False
|
||||
self._soft_device_id = None
|
||||
|
||||
@runs_in_hwd_thread
|
||||
def checkDevice(self):
|
||||
super().checkDevice()
|
||||
self._perform_hw1_preflight()
|
||||
|
||||
def _perform_hw1_preflight(self):
|
||||
assert self.is_hw1()
|
||||
if self._preflightDone:
|
||||
return
|
||||
try:
|
||||
firmwareInfo = self.dongleObject.getFirmwareVersion()
|
||||
firmware = firmwareInfo['version']
|
||||
if versiontuple(firmware) < versiontuple(self.MIN_SUPPORTED_HW1_FW_VERSION):
|
||||
self.close()
|
||||
raise UserFacingException(
|
||||
_("Unsupported device firmware (too old).") + f"\nInstalled: {firmware}. Needed: >={self.MIN_SUPPORTED_HW1_FW_VERSION}")
|
||||
try:
|
||||
self.dongleObject.getOperationMode()
|
||||
except BTChipException as e:
|
||||
if (e.sw == 0x6985):
|
||||
self.close()
|
||||
self.handler.get_setup()
|
||||
# Acquire the new client on the next run
|
||||
else:
|
||||
raise e
|
||||
if self.has_detached_pin_support(self.dongleObject) and not self.is_pin_validated(self.dongleObject):
|
||||
assert self.handler, "no handler for client"
|
||||
remaining_attempts = self.dongleObject.getVerifyPinRemainingAttempts()
|
||||
if remaining_attempts != 1:
|
||||
msg = "Enter your Ledger PIN - remaining attempts : " + str(remaining_attempts)
|
||||
else:
|
||||
msg = "Enter your Ledger PIN - WARNING : LAST ATTEMPT. If the PIN is not correct, the dongle will be wiped."
|
||||
confirmed, p, pin = self.password_dialog(msg)
|
||||
if not confirmed:
|
||||
raise UserFacingException(_('Aborted by user - please unplug the dongle and plug it again before retrying'))
|
||||
pin = pin.encode()
|
||||
self.dongleObject.verifyPin(pin)
|
||||
except BTChipException as e:
|
||||
if (e.sw == 0x6faa):
|
||||
raise UserFacingException(_('Dongle is temporarily locked - please unplug it and replug it again'))
|
||||
if ((e.sw & 0xFFF0) == 0x63c0):
|
||||
raise UserFacingException(_('Invalid PIN - please unplug the dongle and plug it again before retrying'))
|
||||
if e.sw == 0x6f00 and e.message == 'Invalid channel':
|
||||
# based on docs 0x6f00 might be a more general error, hence we also compare message to be sure
|
||||
raise UserFacingException(_("Invalid channel.\nPlease make sure that 'Browser support' is disabled on your device."))
|
||||
if e.sw == 0x6d00 or e.sw == 0x6700:
|
||||
raise UserFacingException(_("Device not in Bitcoin mode")) from e
|
||||
raise e
|
||||
else:
|
||||
deprecation_warning = (
|
||||
"This Ledger device (HW.1) is being deprecated.\n\nIt is no longer supported by Ledger.\n"
|
||||
"Future versions of Electrum will no longer be compatible with it.\n\n"
|
||||
"You should move your coins and migrate to a modern hardware device.")
|
||||
_logger.warning(deprecation_warning.replace("\n", " "))
|
||||
if self.handler:
|
||||
self.handler.show_message(deprecation_warning)
|
||||
self._preflightDone = True
|
||||
|
||||
|
||||
class Ledger_Client_New(Ledger_Client):
|
||||
"""Client based on the ledger_bitcoin library, targeting versions 2.1.* and above."""
|
||||
|
||||
@@ -1333,10 +1206,10 @@ class LedgerPlugin(HW_PluginBase):
|
||||
keystore_class = Ledger_KeyStore
|
||||
minimum_library = (0, 2, 0)
|
||||
maximum_library = (0, 4, 0)
|
||||
DEVICE_IDS = [(0x2581, 0x1807), # HW.1 legacy btchip
|
||||
(0x2581, 0x2b7c), # HW.1 transitional production
|
||||
(0x2581, 0x3b7c), # HW.1 ledger production
|
||||
(0x2581, 0x4b7c), # HW.1 ledger test
|
||||
DEVICE_IDS = [(0x2581, 0x1807), # HW.1 legacy btchip # not supported anymore (but we log an exception)
|
||||
(0x2581, 0x2b7c), # HW.1 transitional production # not supported anymore
|
||||
(0x2581, 0x3b7c), # HW.1 ledger production # not supported anymore
|
||||
(0x2581, 0x4b7c), # HW.1 ledger test # not supported anymore
|
||||
(0x2c97, 0x0000), # Blue
|
||||
(0x2c97, 0x0001), # Nano-S
|
||||
(0x2c97, 0x0004), # Nano-X
|
||||
|
||||
@@ -52,46 +52,9 @@ class Plugin(LedgerPlugin, QtPluginBase):
|
||||
|
||||
|
||||
class Ledger_Handler(QtHandlerBase):
|
||||
setup_signal = pyqtSignal()
|
||||
auth_signal = pyqtSignal(object, object)
|
||||
|
||||
MESSAGE_DIALOG_TITLE = _("Ledger Status")
|
||||
|
||||
def __init__(self, win):
|
||||
super(Ledger_Handler, self).__init__(win, 'Ledger')
|
||||
self.setup_signal.connect(self.setup_dialog)
|
||||
self.auth_signal.connect(self.auth_dialog)
|
||||
|
||||
def word_dialog(self, msg):
|
||||
response = QInputDialog.getText(self.top_level_window(), "Ledger Wallet Authentication", msg, QLineEdit.EchoMode.Password)
|
||||
if not response[1]:
|
||||
self.word = None
|
||||
else:
|
||||
self.word = str(response[0])
|
||||
self.done.set()
|
||||
|
||||
def auth_dialog(self, data, client: 'Ledger_Client'):
|
||||
try:
|
||||
from .auth2fa import LedgerAuthDialog
|
||||
except ImportError as e:
|
||||
self.message_dialog(repr(e))
|
||||
return
|
||||
dialog = LedgerAuthDialog(self, data, client=client)
|
||||
dialog.exec()
|
||||
self.word = dialog.pin
|
||||
self.done.set()
|
||||
|
||||
def get_auth(self, data, *, client: 'Ledger_Client'):
|
||||
self.done.clear()
|
||||
self.auth_signal.emit(data, client)
|
||||
self.done.wait()
|
||||
return self.word
|
||||
|
||||
def get_setup(self):
|
||||
self.done.clear()
|
||||
self.setup_signal.emit()
|
||||
self.done.wait()
|
||||
return
|
||||
|
||||
def setup_dialog(self):
|
||||
self.show_error(_('Initialization of Ledger HW devices is currently disabled.'))
|
||||
|
||||
Reference in New Issue
Block a user