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