URI: 
       tFix cosigner_pool plugin for multiple windows - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 2aa21ece79f1484148263e772509926ea00251a1
   DIR parent 392335487b7a347b191a4b9a4553f364acb58e83
  HTML Author: Neil Booth <kyuupichan@gmail.com>
       Date:   Fri,  4 Sep 2015 12:36:25 +0900
       
       Fix cosigner_pool plugin for multiple windows
       
       Diffstat:
         M lib/plugins.py                      |       4 ++++
         M plugins/cosigner_pool.py            |     162 ++++++++++++++++++-------------
       
       2 files changed, 97 insertions(+), 69 deletions(-)
       ---
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -128,6 +128,7 @@ class Plugins:
        
            def on_close_window(self, window):
                self.windows.remove(window)
       +        self.trigger('on_close_window', window)
        
        hook_names = set()
        hooks = {}
       t@@ -212,5 +213,8 @@ class BasePlugin:
                pass
        
            # Events
       +    def on_close_window(self, window):
       +        pass
       +
            def on_new_window(self, window):
                pass
   DIR diff --git a/plugins/cosigner_pool.py b/plugins/cosigner_pool.py
       t@@ -40,98 +40,115 @@ HOST = 'ecdsa.net'
        server = xmlrpclib.ServerProxy('http://%s:%d'%(HOST,PORT), allow_none=True)
        
        
       -class Listener(threading.Thread):
       +class Listener(util.DaemonThread):
        
            def __init__(self, parent):
       -        threading.Thread.__init__(self)
       +        util.DaemonThread.__init__(self)
                self.daemon = True
                self.parent = parent
       -        self.keyname = None
       -        self.keyhash = None
       -        self.is_running = False
       -        self.message = None
       +        self.received = set()
       +        self.keyhashes = []
       +        self.timeout = 0
        
       -    def set_key(self, keyname, keyhash):
       -        self.keyname = keyname
       -        self.keyhash = keyhash
       -
       -    def clear(self):
       -        server.delete(self.keyhash)
       -        self.message = None
       +    def set_keyhashes(self, keyhashes):
       +        self.keyhashes = keyhashes
        
       +    def clear(self, keyhash):
       +        server.delete(keyhash)
       +        self.received.remove(keyhash)
        
            def run(self):
       -        self.is_running = True
       -        while self.is_running:
       -            if not self.keyhash:
       +        while self.running:
       +            if not self.keyhashes:
                        time.sleep(2)
                        continue
       -            if not self.message:
       +            for keyhash in self.keyhashes:
       +                if keyhash in self.received:
       +                    continue
                        try:
       -                    self.message = server.get(self.keyhash)
       +                    message = server.get(keyhash)
                        except Exception as e:
       -                    util.print_error("cannot contact cosigner pool")
       +                    self.print_error("cannot contact cosigner pool")
                            time.sleep(30)
                            continue
       -                if self.message:
       -                    self.parent.win.emit(SIGNAL("cosigner:receive"))
       +                if message:
       +                    self.received.add(keyhash)
       +                    self.print_error("received message for", keyhash)
       +                    self.parent.obj.emit(SIGNAL("cosigner:receive"), keyhash,
       +                                         message)
                    # poll every 30 seconds
                    time.sleep(30)
        
        
        class Plugin(BasePlugin):
        
       -    wallet = None
       -    listener = None
       +    def __init__(self, parent, config, name):
       +        BasePlugin.__init__(self, parent, config, name)
       +        self.daemon = True
       +        self.listener = None
       +        self.obj = QObject()
       +        self.obj.connect(self.obj, SIGNAL('cosigner:receive'), self.on_receive)
       +        self.keys = []
       +        self.cosigner_list = []
        
       -    @hook
       -    def init_qt(self, gui):
       -        self.win = gui.main_window
       -        self.win.connect(self.win, SIGNAL('cosigner:receive'), self.on_receive)
       +    def on_new_window(self, window):
       +        self.update()
        
       -    def is_available(self):
       -        if self.wallet is None:
       -            return True
       -        return self.wallet.wallet_type in ['2of2', '2of3']
       +    def on_close_window(self, window):
       +        self.update()
        
       -    @hook
       -    def load_wallet(self, wallet, window):
       -        self.wallet = wallet
       -        if not self.is_available():
       -            return
       -        if self.listener is None:
       -            self.listener = Listener(self)
       -            self.listener.start()
       +    def available_wallets(self):
       +        result = {}
       +        for window in self.parent.windows:
       +            if window.wallet.wallet_type in ['2of2', '2of3']:
       +                result[window.wallet] = window
       +        return result
       +
       +    def is_available(self):
       +        return bool(self.available_wallets())
       +
       +    def update(self):
       +        wallets = self.available_wallets()
       +        if wallets:
       +            if self.listener is None:
       +                self.print_error("starting listener")
       +                self.listener = Listener(self)
       +                self.listener.start()
       +        elif self.listener:
       +            self.print_error("shutting down listener")
       +            self.listener.stop()
       +            self.listener = None
       +        self.keys = []
                self.cosigner_list = []
       -        for key, xpub in self.wallet.master_public_keys.items():
       -            K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex')
       -            _hash = bitcoin.Hash(K).encode('hex')
       -            if self.wallet.master_private_keys.get(key):
       -                self.listener.set_key(key, _hash)
       -            else:
       -                self.cosigner_list.append((xpub, K, _hash))
       +        for wallet, window in wallets.items():
       +            for key, xpub in wallet.master_public_keys.items():
       +                K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex')
       +                _hash = bitcoin.Hash(K).encode('hex')
       +                if wallet.master_private_keys.get(key):
       +                    self.keys.append((key, _hash, window))
       +                else:
       +                    self.cosigner_list.append((window, xpub, K, _hash))
       +        if self.listener:
       +            self.listener.set_keyhashes([t[1] for t in self.keys])
        
            @hook
            def transaction_dialog(self, d):
       -        self.send_button = b = QPushButton(_("Send to cosigner"))
       +        d.cosigner_send_button = b = QPushButton(_("Send to cosigner"))
                b.clicked.connect(lambda: self.do_send(d.tx))
                d.buttons.insert(0, b)
                self.transaction_dialog_update(d)
        
            @hook
            def transaction_dialog_update(self, d):
       -        if d.tx.is_complete():
       -            self.send_button.hide()
       +        if d.tx.is_complete() or d.wallet.can_sign(d.tx):
       +            d.cosigner_send_button.hide()
                    return
       -        if self.wallet.can_sign(d.tx):
       -            self.send_button.hide()
       -            return
       -        for xpub, K, _hash in self.cosigner_list:
       -            if self.cosigner_can_sign(d.tx, xpub):
       -                self.send_button.show()
       +        for window, xpub, K, _hash in self.cosigner_list:
       +            if window.wallet == d.wallet and self.cosigner_can_sign(d.tx, xpub):
       +                d.cosigner_send_button.show()
                        break
                else:
       -            self.send_button.hide()
       +            d.cosigner_send_button.hide()
        
            def cosigner_can_sign(self, tx, cosigner_xpub):
                from electrum.transaction import x_to_xpub
       t@@ -145,7 +162,7 @@ class Plugin(BasePlugin):
                return cosigner_xpub in xpub_set
        
            def do_send(self, tx):
       -        for xpub, K, _hash in self.cosigner_list:
       +        for window, xpub, K, _hash in self.cosigner_list:
                    if not self.cosigner_can_sign(tx, xpub):
                        continue
                    message = bitcoin.encrypt_message(tx.raw, K)
       t@@ -153,23 +170,30 @@ class Plugin(BasePlugin):
                        server.put(_hash, message)
                    except Exception as e:
                        traceback.print_exc(file=sys.stdout)
       -                self.win.show_message(str(e))
       +                window.show_message("Failed to send transaction to cosigning pool.")
                        return
       -        self.win.show_message("Your transaction was sent to the cosigning pool.\nOpen your cosigner wallet to retrieve it.")
       +            window.show_message("Your transaction was sent to the cosigning pool.\nOpen your cosigner wallet to retrieve it.")
       +
       +    def on_receive(self, keyhash, message):
       +        self.print_error("signal arrived for", keyhash)
       +        for key, _hash, window in self.keys:
       +            if _hash == keyhash:
       +                break
       +        else:
       +            self.print_error("keyhash not found")
       +            return
        
       -    def on_receive(self):
       -        if self.wallet.use_encryption:
       -            password = self.win.password_dialog('An encrypted transaction was retrieved from cosigning pool.\nPlease enter your password to decrypt it.')
       +        wallet = window.wallet
       +        if wallet.use_encryption:
       +            password = window.password_dialog('An encrypted transaction was retrieved from cosigning pool.\nPlease enter your password to decrypt it.')
                    if not password:
                        return
                else:
                    password = None
       -            if not self.win.question(_("An encrypted transaction was retrieved from cosigning pool.\nDo you want to open it now?")):
       +            if not window.question(_("An encrypted transaction was retrieved from cosigning pool.\nDo you want to open it now?")):
                        return
        
       -        message = self.listener.message
       -        key = self.listener.keyname
       -        xprv = self.wallet.get_master_private_key(key, password)
       +        xprv = wallet.get_master_private_key(key, password)
                if not xprv:
                    return
                try:
       t@@ -178,9 +202,9 @@ class Plugin(BasePlugin):
                    message = EC.decrypt_message(message)
                except Exception as e:
                    traceback.print_exc(file=sys.stdout)
       -            self.win.show_message(str(e))
       +            window.show_message(str(e))
                    return
        
       -        self.listener.clear()
       +        self.listener.clear(keyhash)
                tx = transaction.Transaction(message)
       -        show_transaction(tx, self.win, prompt_if_unsaved=True)
       +        show_transaction(tx, window, prompt_if_unsaved=True)