Ledger: move get_client() to the plugin

Mirrors the trezor code
This commit is contained in:
Neil Booth 2016-01-11 15:08:12 +09:00
parent deccca1827
commit 986e198e87
2 changed files with 86 additions and 82 deletions

View File

@ -42,8 +42,6 @@ class BTChipWallet(BIP44_Wallet):
self.handler = None self.handler = None
self.force_watching_only = False self.force_watching_only = False
self.transport = None
self.client = None
self.device_checked = False self.device_checked = False
self.signing = False self.signing = False
@ -53,8 +51,8 @@ class BTChipWallet(BIP44_Wallet):
QMessageBox.warning(QDialog(), _('Warning'), _(message), _('OK')) QMessageBox.warning(QDialog(), _('Warning'), _(message), _('OK'))
else: else:
self.signing = False self.signing = False
if clear_client and self.client is not None: if clear_client:
self.client.bad = True self.plugin.client = None
self.device_checked = False self.device_checked = False
raise Exception(message) raise Exception(message)
@ -76,75 +74,7 @@ class BTChipWallet(BIP44_Wallet):
return BIP44_Wallet.address_id(self, address)[2:] return BIP44_Wallet.address_id(self, address)[2:]
def get_client(self, noPin=False): def get_client(self, noPin=False):
if not BTCHIP: return self.plugin.get_client(self, noPin=noPin)
self.give_error('please install github.com/btchip/btchip-python')
aborted = False
if not self.client or self.client.bad:
try:
d = getDongle(BTCHIP_DEBUG)
self.client = btchip(d)
firmware = self.client.getFirmwareVersion()['version'].split(".")
if not checkFirmware(firmware):
d.close()
try:
updateFirmware()
except Exception, e:
aborted = True
raise e
d = getDongle(BTCHIP_DEBUG)
self.client = btchip(d)
try:
self.client.getOperationMode()
except BTChipException, e:
if (e.sw == 0x6985):
d.close()
dialog = StartBTChipPersoDialog()
dialog.exec_()
# Then fetch the reference again as it was invalidated
d = getDongle(BTCHIP_DEBUG)
self.client = btchip(d)
else:
raise e
if not noPin:
# Immediately prompts for the PIN
remaining_attempts = self.client.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:
aborted = True
raise Exception('Aborted by user - please unplug the dongle and plug it again before retrying')
pin = pin.encode()
self.client.verifyPin(pin)
except BTChipException, e:
try:
self.client.dongle.close()
except:
pass
self.client = None
if (e.sw == 0x6faa):
raise Exception("Dongle is temporarily locked - please unplug it and replug it again")
if ((e.sw & 0xFFF0) == 0x63c0):
raise Exception("Invalid PIN - please unplug the dongle and plug it again before retrying")
raise e
except Exception, e:
try:
self.client.dongle.close()
except:
pass
self.client = None
if not aborted:
raise Exception("Could not connect to your Ledger wallet. Please verify access permissions, PIN, or unplug the dongle and plug it again")
else:
raise e
self.client.bad = False
self.device_checked = False
self.proper_device = False
return self.client
def derive_xkeys(self, root, derivation, password): def derive_xkeys(self, root, derivation, password):
derivation = '/'.join(derivation.split('/')[1:]) derivation = '/'.join(derivation.split('/')[1:])
@ -193,7 +123,8 @@ class BTChipWallet(BIP44_Wallet):
def sign_message(self, address, message, password): def sign_message(self, address, message, password):
use2FA = False use2FA = False
self.signing = True self.signing = True
self.get_client() # prompt for the PIN before displaying the dialog if necessary # prompt for the PIN before displaying the dialog if necessary
client = self.get_client()
if not self.check_proper_device(): if not self.check_proper_device():
self.give_error('Wrong device or password') self.give_error('Wrong device or password')
address_path = self.address_id(address) address_path = self.address_id(address)
@ -208,7 +139,7 @@ class BTChipWallet(BIP44_Wallet):
if not confirmed: if not confirmed:
raise Exception('Aborted by user') raise Exception('Aborted by user')
pin = pin.encode() pin = pin.encode()
self.client.bad = True client.bad = True
self.device_checked = False self.device_checked = False
self.get_client(True) self.get_client(True)
signature = self.get_client().signMessageSign(pin) signature = self.get_client().signMessageSign(pin)
@ -221,7 +152,7 @@ class BTChipWallet(BIP44_Wallet):
self.give_error(e, True) self.give_error(e, True)
finally: finally:
self.handler.stop() self.handler.stop()
self.client.bad = use2FA client.bad = use2FA
self.signing = False self.signing = False
# Parse the ASN.1 signature # Parse the ASN.1 signature
@ -334,7 +265,7 @@ class BTChipWallet(BIP44_Wallet):
if not confirmed: if not confirmed:
raise Exception('Aborted by user') raise Exception('Aborted by user')
pin = pin.encode() pin = pin.encode()
self.client.bad = True client.bad = True
self.device_checked = False self.device_checked = False
self.get_client(True) self.get_client(True)
self.handler.show_message("Signing ...") self.handler.show_message("Signing ...")
@ -361,7 +292,7 @@ class BTChipWallet(BIP44_Wallet):
updatedTransaction = format_transaction(transactionOutput, preparedTrustedInputs) updatedTransaction = format_transaction(transactionOutput, preparedTrustedInputs)
updatedTransaction = hexlify(updatedTransaction) updatedTransaction = hexlify(updatedTransaction)
tx.update(updatedTransaction) tx.update(updatedTransaction)
self.client.bad = use2FA client.bad = use2FA
self.signing = False self.signing = False
def check_proper_device(self): def check_proper_device(self):
@ -404,6 +335,7 @@ class LedgerPlugin(BasePlugin):
BasePlugin.__init__(self, parent, config, name) BasePlugin.__init__(self, parent, config, name)
self.wallet_class.plugin = self self.wallet_class.plugin = self
self.device = self.wallet_class.device self.device = self.wallet_class.device
self.client = None
def is_enabled(self): def is_enabled(self):
return BTCHIP return BTCHIP
@ -441,3 +373,74 @@ class LedgerPlugin(BasePlugin):
@hook @hook
def close_wallet(self, wallet): def close_wallet(self, wallet):
self.client = None self.client = None
def get_client(self, wallet, noPin=False):
aborted = False
client = self.client
if not client or client.bad:
try:
d = getDongle(BTCHIP_DEBUG)
client = btchip(d)
firmware = client.getFirmwareVersion()['version'].split(".")
if not checkFirmware(firmware):
d.close()
try:
updateFirmware()
except Exception, e:
aborted = True
raise e
d = getDongle(BTCHIP_DEBUG)
client = btchip(d)
try:
client.getOperationMode()
except BTChipException, e:
if (e.sw == 0x6985):
d.close()
dialog = StartBTChipPersoDialog()
dialog.exec_()
# Then fetch the reference again as it was invalidated
d = getDongle(BTCHIP_DEBUG)
client = btchip(d)
else:
raise e
if not noPin:
# Immediately prompts for the PIN
remaining_attempts = client.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 = wallet.password_dialog(msg)
if not confirmed:
aborted = True
raise Exception('Aborted by user - please unplug the dongle and plug it again before retrying')
pin = pin.encode()
client.verifyPin(pin)
except BTChipException, e:
try:
client.dongle.close()
except:
pass
client = None
if (e.sw == 0x6faa):
raise Exception("Dongle is temporarily locked - please unplug it and replug it again")
if ((e.sw & 0xFFF0) == 0x63c0):
raise Exception("Invalid PIN - please unplug the dongle and plug it again before retrying")
raise e
except Exception, e:
try:
client.dongle.close()
except:
pass
client = None
if not aborted:
raise Exception("Could not connect to your Ledger wallet. Please verify access permissions, PIN, or unplug the dongle and plug it again")
else:
raise e
client.bad = False
wallet.device_checked = False
wallet.proper_device = False
self.client = client
return self.client

View File

@ -1,11 +1,12 @@
import threading
from PyQt4.Qt import (QDialog, QInputDialog, QLineEdit, from PyQt4.Qt import (QDialog, QInputDialog, QLineEdit,
QVBoxLayout, QLabel, SIGNAL) QVBoxLayout, QLabel, SIGNAL)
import PyQt4.QtCore as QtCore import PyQt4.QtCore as QtCore
import threading
from electrum.plugins import BasePlugin, hook from electrum.i18n import _
from electrum.plugins import hook
from ledger import LedgerPlugin, BTChipWallet from .ledger import LedgerPlugin, BTChipWallet
class Plugin(LedgerPlugin): class Plugin(LedgerPlugin):