URI: 
       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()
       -