Browse Source

Fix cosigner_pool plugin for multiple windows

283
Neil Booth 9 years ago
parent
commit
2aa21ece79
  1. 4
      lib/plugins.py
  2. 162
      plugins/cosigner_pool.py

4
lib/plugins.py

@ -128,6 +128,7 @@ class Plugins:
def on_close_window(self, window): def on_close_window(self, window):
self.windows.remove(window) self.windows.remove(window)
self.trigger('on_close_window', window)
hook_names = set() hook_names = set()
hooks = {} hooks = {}
@ -212,5 +213,8 @@ class BasePlugin:
pass pass
# Events # Events
def on_close_window(self, window):
pass
def on_new_window(self, window): def on_new_window(self, window):
pass pass

162
plugins/cosigner_pool.py

@ -40,98 +40,115 @@ HOST = 'ecdsa.net'
server = xmlrpclib.ServerProxy('http://%s:%d'%(HOST,PORT), allow_none=True) server = xmlrpclib.ServerProxy('http://%s:%d'%(HOST,PORT), allow_none=True)
class Listener(threading.Thread): class Listener(util.DaemonThread):
def __init__(self, parent): def __init__(self, parent):
threading.Thread.__init__(self) util.DaemonThread.__init__(self)
self.daemon = True self.daemon = True
self.parent = parent self.parent = parent
self.keyname = None self.received = set()
self.keyhash = None self.keyhashes = []
self.is_running = False self.timeout = 0
self.message = None
def set_key(self, keyname, keyhash): def set_keyhashes(self, keyhashes):
self.keyname = keyname self.keyhashes = keyhashes
self.keyhash = keyhash
def clear(self):
server.delete(self.keyhash)
self.message = None
def clear(self, keyhash):
server.delete(keyhash)
self.received.remove(keyhash)
def run(self): def run(self):
self.is_running = True while self.running:
while self.is_running: if not self.keyhashes:
if not self.keyhash:
time.sleep(2) time.sleep(2)
continue continue
if not self.message: for keyhash in self.keyhashes:
if keyhash in self.received:
continue
try: try:
self.message = server.get(self.keyhash) message = server.get(keyhash)
except Exception as e: except Exception as e:
util.print_error("cannot contact cosigner pool") self.print_error("cannot contact cosigner pool")
time.sleep(30) time.sleep(30)
continue continue
if self.message: if message:
self.parent.win.emit(SIGNAL("cosigner:receive")) self.received.add(keyhash)
self.print_error("received message for", keyhash)
self.parent.obj.emit(SIGNAL("cosigner:receive"), keyhash,
message)
# poll every 30 seconds # poll every 30 seconds
time.sleep(30) time.sleep(30)
class Plugin(BasePlugin): class Plugin(BasePlugin):
wallet = None def __init__(self, parent, config, name):
listener = None 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 on_new_window(self, window):
def init_qt(self, gui): self.update()
self.win = gui.main_window
self.win.connect(self.win, SIGNAL('cosigner:receive'), self.on_receive)
def is_available(self): def on_close_window(self, window):
if self.wallet is None: self.update()
return True
return self.wallet.wallet_type in ['2of2', '2of3']
@hook def available_wallets(self):
def load_wallet(self, wallet, window): result = {}
self.wallet = wallet for window in self.parent.windows:
if not self.is_available(): if window.wallet.wallet_type in ['2of2', '2of3']:
return result[window.wallet] = window
if self.listener is None: return result
self.listener = Listener(self)
self.listener.start() 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 = [] self.cosigner_list = []
for key, xpub in self.wallet.master_public_keys.items(): for wallet, window in wallets.items():
K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex') for key, xpub in wallet.master_public_keys.items():
_hash = bitcoin.Hash(K).encode('hex') K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex')
if self.wallet.master_private_keys.get(key): _hash = bitcoin.Hash(K).encode('hex')
self.listener.set_key(key, _hash) if wallet.master_private_keys.get(key):
else: self.keys.append((key, _hash, window))
self.cosigner_list.append((xpub, K, _hash)) else:
self.cosigner_list.append((window, xpub, K, _hash))
if self.listener:
self.listener.set_keyhashes([t[1] for t in self.keys])
@hook @hook
def transaction_dialog(self, d): 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)) b.clicked.connect(lambda: self.do_send(d.tx))
d.buttons.insert(0, b) d.buttons.insert(0, b)
self.transaction_dialog_update(d) self.transaction_dialog_update(d)
@hook @hook
def transaction_dialog_update(self, d): def transaction_dialog_update(self, d):
if d.tx.is_complete(): if d.tx.is_complete() or d.wallet.can_sign(d.tx):
self.send_button.hide() d.cosigner_send_button.hide()
return return
if self.wallet.can_sign(d.tx): for window, xpub, K, _hash in self.cosigner_list:
self.send_button.hide() if window.wallet == d.wallet and self.cosigner_can_sign(d.tx, xpub):
return d.cosigner_send_button.show()
for xpub, K, _hash in self.cosigner_list:
if self.cosigner_can_sign(d.tx, xpub):
self.send_button.show()
break break
else: else:
self.send_button.hide() d.cosigner_send_button.hide()
def cosigner_can_sign(self, tx, cosigner_xpub): def cosigner_can_sign(self, tx, cosigner_xpub):
from electrum.transaction import x_to_xpub from electrum.transaction import x_to_xpub
@ -145,7 +162,7 @@ class Plugin(BasePlugin):
return cosigner_xpub in xpub_set return cosigner_xpub in xpub_set
def do_send(self, tx): 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): if not self.cosigner_can_sign(tx, xpub):
continue continue
message = bitcoin.encrypt_message(tx.raw, K) message = bitcoin.encrypt_message(tx.raw, K)
@ -153,23 +170,30 @@ class Plugin(BasePlugin):
server.put(_hash, message) server.put(_hash, message)
except Exception as e: except Exception as e:
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
self.win.show_message(str(e)) window.show_message("Failed to send transaction to cosigning pool.")
return 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): wallet = window.wallet
if self.wallet.use_encryption: if wallet.use_encryption:
password = self.win.password_dialog('An encrypted transaction was retrieved from cosigning pool.\nPlease enter your password to decrypt it.') password = window.password_dialog('An encrypted transaction was retrieved from cosigning pool.\nPlease enter your password to decrypt it.')
if not password: if not password:
return return
else: else:
password = None 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 return
message = self.listener.message xprv = wallet.get_master_private_key(key, password)
key = self.listener.keyname
xprv = self.wallet.get_master_private_key(key, password)
if not xprv: if not xprv:
return return
try: try:
@ -178,9 +202,9 @@ class Plugin(BasePlugin):
message = EC.decrypt_message(message) message = EC.decrypt_message(message)
except Exception as e: except Exception as e:
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
self.win.show_message(str(e)) window.show_message(str(e))
return return
self.listener.clear() self.listener.clear(keyhash)
tx = transaction.Transaction(message) tx = transaction.Transaction(message)
show_transaction(tx, self.win, prompt_if_unsaved=True) show_transaction(tx, window, prompt_if_unsaved=True)

Loading…
Cancel
Save