electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 2aa21ece79f1484148263e772509926ea00251a1
parent 392335487b7a347b191a4b9a4553f364acb58e83
Author: Neil Booth <kyuupichan@gmail.com>
Date:   Fri,  4 Sep 2015 12:36:25 +0900

Fix cosigner_pool plugin for multiple windows

Diffstat:
Mlib/plugins.py | 4++++
Mplugins/cosigner_pool.py | 162+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
2 files changed, 97 insertions(+), 69 deletions(-)

diff --git a/lib/plugins.py b/lib/plugins.py @@ -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 = {} @@ -212,5 +213,8 @@ class BasePlugin: pass # Events + def on_close_window(self, window): + pass + def on_new_window(self, window): pass diff --git a/plugins/cosigner_pool.py b/plugins/cosigner_pool.py @@ -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 @@ -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) @@ -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: @@ -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)