ttrezor plugin: move Qt callbacks in a handler - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 21ccb1e82de79ec5277e2f9ec55c30166d7c1475 DIR parent f14c863a0acc089b68ef0cbe19d2104672e38956 HTML Author: ThomasV <thomasv@gitorious> Date: Sat, 4 Apr 2015 15:13:56 +0200 ttrezor plugin: move Qt callbacks in a handler Diffstat: M plugins/trezor.py | 155 +++++++++++++++++-------------- 1 file changed, 83 insertions(+), 72 deletions(-) --- DIR diff --git a/plugins/trezor.py b/plugins/trezor.py t@@ -6,6 +6,7 @@ from sys import stderr from time import sleep from base64 import b64encode, b64decode import unicodedata +import threading import electrum from electrum.account import BIP32_Account t@@ -15,6 +16,7 @@ from electrum.plugins import BasePlugin, hook from electrum.transaction import deserialize from electrum.wallet import BIP32_HD_Wallet from electrum.util import print_error +from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root from electrum_gui.qt.util import * t@@ -37,20 +39,6 @@ def give_error(message): raise Exception(message) -def trezor_passphrase_dialog(msg): - from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog - d = QDialog() - d.setModal(1) - d.setLayout(make_password_dialog(d, None, msg, False)) - confirmed, p, passphrase = run_password_dialog(d, None, None) - if not confirmed: - return None - if passphrase is None: - passphrase = '' # Even blank string is valid Trezor passphrase - passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) - return passphrase - - class Plugin(BasePlugin): def fullname(self): t@@ -103,10 +91,6 @@ class Plugin(BasePlugin): return True @hook - def init_qt(self, gui): - self.window = gui.main_window - - @hook def close_wallet(self): print_error("trezor: clear session") if self.wallet and self.wallet.client: t@@ -115,6 +99,8 @@ class Plugin(BasePlugin): @hook def load_wallet(self, wallet): + self.twd = TrezorQtHandler(self.window) + self.wallet.twd = self.twd if self.trezor_is_connected(): if not self.wallet.check_proper_device(): QMessageBox.information(self.window, _('Error'), _("This wallet does not match your Trezor device"), _('OK')) t@@ -132,9 +118,8 @@ class Plugin(BasePlugin): return wallet = TrezorWallet(storage) self.wallet = wallet - passphrase = trezor_passphrase_dialog(_("Please enter your Trezor passphrase.") + '\n' + _("Press OK if you do not use one.")) + passphrase = self.twd.get_passphrase(_("Please enter your Trezor passphrase.") + '\n' + _("Press OK if you do not use one.")) if passphrase is None: - QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK')) return password = wizard.password_dialog() wallet.add_seed(seed, password) t@@ -145,13 +130,6 @@ class Plugin(BasePlugin): return wallet @hook - def send_tx(self, tx): - tx.error = None - try: - self.wallet.trezor_sign(tx) - except Exception as e: - tx.error = str(e) - @hook def receive_menu(self, menu, addrs): if not self.wallet.is_watching_only() and self.wallet.atleast_version(1, 3) and len(addrs) == 1: menu.addAction(_("Show on TREZOR"), lambda: self.wallet.show_address(addrs[0])) t@@ -174,9 +152,9 @@ class Plugin(BasePlugin): if not response[1]: return new_label = str(response[0]) - twd.start("Please confirm label change on Trezor") + self.twd.show_message("Please confirm label change on Trezor") status = self.wallet.get_client().apply_settings(label=new_label) - twd.stop() + self.twd.stop() update_label() current_label_label = QLabel() t@@ -192,7 +170,6 @@ class Plugin(BasePlugin): return False -from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root class TrezorWallet(BIP32_HD_Wallet): wallet_type = 'trezor' t@@ -213,6 +190,11 @@ class TrezorWallet(BIP32_HD_Wallet): def can_import(self): return False + def can_sign_xpubkey(self, x_pubkey): + xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey) + print "z", xpub + return xpub in self.master_public_keys.values() + def can_export(self): return False t@@ -236,6 +218,7 @@ class TrezorWallet(BIP32_HD_Wallet): except: give_error('Could not connect to your Trezor. Please verify the cable is connected and that no other app is using it.') self.client = QtGuiTrezorClient(self.transport) + self.client.twd = self.twd self.client.set_tx_api(self) #self.client.clear_session()# TODO Doesn't work with firmware 1.1, returns proto.Failure self.client.bad = False t@@ -306,7 +289,7 @@ class TrezorWallet(BIP32_HD_Wallet): #except Exception, e: # give_error(e) #finally: - # twd.emit(SIGNAL('trezor_done')) + # twd.stop() #return str(decrypted_msg) def show_address(self, address): t@@ -322,7 +305,7 @@ class TrezorWallet(BIP32_HD_Wallet): except Exception, e: give_error(e) finally: - twd.emit(SIGNAL('trezor_done')) + self.twd.stop() def sign_message(self, address, message, password): if not self.check_proper_device(): t@@ -337,21 +320,16 @@ class TrezorWallet(BIP32_HD_Wallet): except Exception, e: give_error(e) finally: - twd.emit(SIGNAL('trezor_done')) + self.twd.stop() b64_msg_sig = b64encode(msg_sig.signature) return str(b64_msg_sig) def sign_transaction(self, tx, password): - # the tx is signed by trezor_sign, in the GUI thread - if tx.error: - raise BaseException(tx.error) - - def trezor_sign(self, tx): if tx.is_complete(): return if not self.check_proper_device(): give_error('Wrong device or password') - + client = self.get_client() inputs = self.tx_inputs(tx) outputs = self.tx_outputs(tx) try: t@@ -359,16 +337,15 @@ class TrezorWallet(BIP32_HD_Wallet): except Exception, e: give_error(e) finally: - twd.emit(SIGNAL('trezor_done')) - values = [i['value'] for i in tx.inputs] + self.twd.stop() + #values = [i['value'] for i in tx.inputs] raw = signed_tx.encode('hex') tx.update(raw) - for i, txinput in enumerate(tx.inputs): - txinput['value'] = values[i] + #for i, txinput in enumerate(tx.inputs): + # txinput['value'] = values[i] def tx_inputs(self, tx): inputs = [] - for txinput in tx.inputs: txinputtype = types.TxInputType() if ('is_coinbase' in txinput and txinput['is_coinbase']): t@@ -442,6 +419,7 @@ class TrezorWallet(BIP32_HD_Wallet): def get_tx(self, tx_hash): tx = self.transactions[tx_hash] + tx.deserialize() return self.electrum_tx_to_txtype(tx) def check_proper_device(self): t@@ -461,10 +439,10 @@ class TrezorWallet(BIP32_HD_Wallet): return self.proper_device -class TrezorQtGuiMixin(object): +class TrezorGuiMixin(object): def __init__(self, *args, **kwargs): - super(TrezorQtGuiMixin, self).__init__(*args, **kwargs) + super(TrezorGuiMixin, self).__init__(*args, **kwargs) def callback_ButtonRequest(self, msg): if msg.code == 3: t@@ -477,7 +455,7 @@ class TrezorQtGuiMixin(object): message = "Confirm address on Trezor device to continue" else: message = "Check Trezor device to continue" - twd.start(message) + self.twd.show_message(message) return proto.ButtonAck() def callback_PinMatrixRequest(self, msg): t@@ -489,17 +467,15 @@ class TrezorQtGuiMixin(object): desc = 'new PIN again' else: desc = 'PIN' - - pin = self.pin_dialog(msg="Please enter Trezor %s" % desc) + pin = self.twd.get_pin("Please enter Trezor %s" % desc) if not pin: return proto.Cancel() return proto.PinMatrixAck(pin=pin) def callback_PassphraseRequest(self, req): msg = _("Please enter your Trezor passphrase.") - passphrase = trezor_passphrase_dialog(msg) + passphrase = self.twd.get_passphrase(msg) if passphrase is None: - QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK')) return proto.Cancel() return proto.PassphraseAck(passphrase=passphrase) t@@ -509,47 +485,85 @@ class TrezorQtGuiMixin(object): word = raw_input() return proto.WordAck(word=word) - def pin_dialog(self, msg): + +class TrezorQtHandler: + + def __init__(self, win): + self.win = win + self.win.connect(win, SIGNAL('trezor_done'), self.dialog_stop) + self.win.connect(win, SIGNAL('message_dialog'), self.message_dialog) + self.win.connect(win, SIGNAL('pin_dialog'), self.pin_dialog) + self.win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog) + self.done = threading.Event() + + def stop(self): + self.win.emit(SIGNAL('trezor_done')) + + def show_message(self, msg): + self.message = msg + self.win.emit(SIGNAL('message_dialog')) + + def get_pin(self, msg): + self.done.clear() + self.message = msg + self.win.emit(SIGNAL('pin_dialog')) + self.done.wait() + return self.response + + def get_passphrase(self, msg): + self.done.clear() + self.message = msg + self.win.emit(SIGNAL('passphrase_dialog')) + self.done.wait() + return self.passphrase + + def pin_dialog(self): d = QDialog(None) d.setModal(1) d.setWindowTitle(_("Enter PIN")) d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) matrix = PinMatrixWidget() - vbox = QVBoxLayout() - vbox.addWidget(QLabel(msg)) + vbox.addWidget(QLabel(self.message)) vbox.addWidget(matrix) vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) d.setLayout(vbox) + if not d.exec_(): + self.response = None + self.response = str(matrix.get_value()) + self.done.set() - if not d.exec_(): return - return str(matrix.get_value()) - -class TrezorWaitingDialog(QThread): - def __init__(self): - QThread.__init__(self) - self.waiting = False + def passphrase_dialog(self): + from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog + d = QDialog() + d.setModal(1) + d.setLayout(make_password_dialog(d, None, self.message, False)) + confirmed, p, passphrase = run_password_dialog(d, None, None) + if not confirmed: + QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK')) + self.passphrase = None + else: + if passphrase is None: + passphrase = '' # Even blank string is valid Trezor passphrase + self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) + self.done.set() - def start(self, message): + def message_dialog(self): self.d = QDialog() self.d.setModal(1) self.d.setWindowTitle('Please Check Trezor Device') self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) - l = QLabel(message) + l = QLabel(self.message) vbox = QVBoxLayout(self.d) vbox.addWidget(l) self.d.show() - if not self.waiting: - self.waiting = True - self.d.connect(twd, SIGNAL('trezor_done'), self.stop) - def stop(self): + def dialog_stop(self): self.d.hide() - self.waiting = False if TREZOR: - class QtGuiTrezorClient(ProtocolMixin, TrezorQtGuiMixin, BaseClient): + class QtGuiTrezorClient(ProtocolMixin, TrezorGuiMixin, BaseClient): def call_raw(self, msg): try: resp = BaseClient.call_raw(self, msg) t@@ -558,6 +572,3 @@ if TREZOR: raise return resp - - twd = TrezorWaitingDialog() -