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

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

Loading…
Cancel
Save