Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -45,42 +45,36 @@ class Listener(threading.Thread):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.parent = parent
|
||||
self.key = None
|
||||
self.keyname = None
|
||||
self.keyhash = None
|
||||
self.is_running = False
|
||||
self.message = None
|
||||
self.delete = False
|
||||
|
||||
def set_key(self, key):
|
||||
self.key = key
|
||||
|
||||
def set_key(self, keyname, keyhash):
|
||||
self.keyname = keyname
|
||||
self.keyhash = keyhash
|
||||
|
||||
def clear(self):
|
||||
self.delete = True
|
||||
server.delete(self.keyhash)
|
||||
self.message = None
|
||||
|
||||
|
||||
def run(self):
|
||||
self.is_running = True
|
||||
while self.is_running:
|
||||
|
||||
if not self.key:
|
||||
if not self.keyhash:
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
if not self.message:
|
||||
try:
|
||||
self.message = server.get(self.key)
|
||||
self.message = server.get(self.keyhash)
|
||||
except Exception as e:
|
||||
util.print_error("cannot contact cosigner pool")
|
||||
time.sleep(30)
|
||||
continue
|
||||
|
||||
if self.message:
|
||||
self.parent.win.emit(SIGNAL("cosigner:receive"))
|
||||
else:
|
||||
if self.delete:
|
||||
# save it to disk
|
||||
server.delete(self.key)
|
||||
self.message = None
|
||||
self.delete = False
|
||||
|
||||
# poll every 30 seconds
|
||||
time.sleep(30)
|
||||
|
||||
|
||||
@@ -109,21 +103,25 @@ class Plugin(BasePlugin):
|
||||
self.load_wallet(self.win.wallet)
|
||||
return True
|
||||
|
||||
def is_available(self):
|
||||
if self.wallet is None:
|
||||
return True
|
||||
return self.wallet.wallet_type in ['2of2', '2of3']
|
||||
|
||||
def load_wallet(self, wallet):
|
||||
self.wallet = wallet
|
||||
if not self.is_available():
|
||||
return
|
||||
mpk = self.wallet.get_master_public_keys()
|
||||
|
||||
self.cold = mpk.get('x2')
|
||||
if self.cold:
|
||||
self.cold_K = bitcoin.deserialize_xkey(self.cold)[-1].encode('hex')
|
||||
self.cold_hash = bitcoin.Hash(self.cold_K).encode('hex')
|
||||
|
||||
self.hot = mpk.get('x1')
|
||||
if self.hot:
|
||||
self.hot_K = bitcoin.deserialize_xkey(self.hot)[-1].encode('hex')
|
||||
self.hot_hash = bitcoin.Hash(self.hot_K).encode('hex')
|
||||
self.listener.set_key(self.hot_hash)
|
||||
|
||||
self.cosigner_list = []
|
||||
for key, xpub in mpk.items():
|
||||
keyname = key + '/' # fixme
|
||||
K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex')
|
||||
_hash = bitcoin.Hash(K).encode('hex')
|
||||
if self.wallet.master_private_keys.get(keyname):
|
||||
self.listener.set_key(keyname, _hash)
|
||||
else:
|
||||
self.cosigner_list.append((xpub, K, _hash))
|
||||
|
||||
def transaction_dialog(self, d):
|
||||
self.send_button = b = QPushButton(_("Send to cosigner"))
|
||||
@@ -131,18 +129,18 @@ class Plugin(BasePlugin):
|
||||
d.buttons.insertWidget(2, b)
|
||||
self.transaction_dialog_update(d)
|
||||
|
||||
|
||||
def transaction_dialog_update(self, d):
|
||||
if d.tx.is_complete():
|
||||
self.send_button.hide()
|
||||
return
|
||||
if self.cosigner_can_sign(d.tx):
|
||||
self.send_button.show()
|
||||
for xpub, K, _hash in self.cosigner_list:
|
||||
if self.cosigner_can_sign(d.tx, xpub):
|
||||
self.send_button.show()
|
||||
break
|
||||
else:
|
||||
self.send_button.hide()
|
||||
|
||||
|
||||
def cosigner_can_sign(self, tx):
|
||||
def cosigner_can_sign(self, tx, cosigner_xpub):
|
||||
from electrum.transaction import x_to_xpub
|
||||
xpub_set = set([])
|
||||
for txin in tx.inputs:
|
||||
@@ -151,24 +149,21 @@ class Plugin(BasePlugin):
|
||||
if xpub:
|
||||
xpub_set.add(xpub)
|
||||
|
||||
return self.cold in xpub_set
|
||||
|
||||
return cosigner_xpub in xpub_set
|
||||
|
||||
def do_send(self, tx):
|
||||
if not self.cosigner_can_sign(tx):
|
||||
return
|
||||
|
||||
message = bitcoin.encrypt_message(tx.raw, self.cold_K)
|
||||
|
||||
try:
|
||||
server.put(self.cold_hash, message)
|
||||
self.win.show_message("Your transaction was sent to the cosigning pool.\nOpen your cosigner wallet to retrieve it.")
|
||||
except Exception as e:
|
||||
self.win.show_message(str(e))
|
||||
|
||||
for xpub, K, _hash in self.cosigner_list:
|
||||
if not self.cosigner_can_sign(tx, xpub):
|
||||
continue
|
||||
message = bitcoin.encrypt_message(tx.raw, K)
|
||||
try:
|
||||
server.put(_hash, message)
|
||||
except Exception as e:
|
||||
self.win.show_message(str(e))
|
||||
return
|
||||
self.win.show_message("Your transaction was sent to the cosigning pool.\nOpen your cosigner wallet to retrieve it.")
|
||||
|
||||
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.')
|
||||
if not password:
|
||||
@@ -177,11 +172,12 @@ class Plugin(BasePlugin):
|
||||
password = None
|
||||
|
||||
message = self.listener.message
|
||||
xpriv = self.wallet.get_master_private_key('x1/', password)
|
||||
if not xpriv:
|
||||
key = self.listener.keyname
|
||||
xprv = self.wallet.get_master_private_key(key, password)
|
||||
if not xprv:
|
||||
return
|
||||
try:
|
||||
k = bitcoin.deserialize_xkey(xpriv)[-1].encode('hex')
|
||||
k = bitcoin.deserialize_xkey(xprv)[-1].encode('hex')
|
||||
EC = bitcoin.EC_KEY(k.decode('hex'))
|
||||
message = EC.decrypt_message(message)
|
||||
except Exception as e:
|
||||
@@ -190,7 +186,6 @@ class Plugin(BasePlugin):
|
||||
return
|
||||
|
||||
self.listener.clear()
|
||||
|
||||
tx = transaction.Transaction.deserialize(message)
|
||||
self.win.show_transaction(tx)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL
|
||||
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
|
||||
import PyQt4.QtCore as QtCore
|
||||
from binascii import unhexlify
|
||||
from struct import pack
|
||||
@@ -7,7 +7,7 @@ from time import sleep
|
||||
from base64 import b64encode, b64decode
|
||||
|
||||
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
|
||||
from electrum_gui.qt.util import ok_cancel_buttons
|
||||
from electrum_gui.qt.util import ok_cancel_buttons, EnterButton
|
||||
from electrum.account import BIP32_Account
|
||||
from electrum.bitcoin import EncodeBase58Check, public_key_to_bc_address, bc_address_to_hash_160
|
||||
from electrum.i18n import _
|
||||
@@ -43,6 +43,7 @@ class Plugin(BasePlugin):
|
||||
def __init__(self, gui, name):
|
||||
BasePlugin.__init__(self, gui, name)
|
||||
self._is_available = self._init()
|
||||
self._requires_settings = True
|
||||
self.wallet = None
|
||||
|
||||
def _init(self):
|
||||
@@ -55,6 +56,9 @@ class Plugin(BasePlugin):
|
||||
return True
|
||||
return False
|
||||
|
||||
def requires_settings(self):
|
||||
return self._requires_settings
|
||||
|
||||
def set_enabled(self, enabled):
|
||||
self.wallet.storage.put('use_' + self.name, enabled)
|
||||
|
||||
@@ -77,6 +81,8 @@ class Plugin(BasePlugin):
|
||||
wallet_types.append(('trezor', _("Trezor wallet"), TrezorWallet))
|
||||
|
||||
def installwizard_restore(self, wizard, storage):
|
||||
if storage.get('wallet_type') != 'trezor':
|
||||
return
|
||||
wallet = TrezorWallet(storage)
|
||||
try:
|
||||
wallet.create_main_account(None)
|
||||
@@ -91,6 +97,41 @@ class Plugin(BasePlugin):
|
||||
except Exception as e:
|
||||
tx.error = str(e)
|
||||
|
||||
def settings_widget(self, window):
|
||||
return EnterButton(_('Settings'), self.settings_dialog)
|
||||
|
||||
def settings_dialog(self):
|
||||
get_label = lambda: self.wallet.get_client().features.label
|
||||
update_label = lambda: current_label_label.setText("Label: %s" % get_label())
|
||||
|
||||
d = QDialog()
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel("Trezor Options"),0,0)
|
||||
layout.addWidget(QLabel("ID:"),1,0)
|
||||
layout.addWidget(QLabel(" %s" % self.wallet.get_client().get_device_id()),1,1)
|
||||
|
||||
def modify_label():
|
||||
response = QInputDialog().getText(None, "Set New Trezor Label", "New Trezor Label: (upon submission confirm on Trezor)")
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
twd.start("Please confirm label change on Trezor")
|
||||
status = self.wallet.get_client().apply_settings(label=new_label)
|
||||
twd.stop()
|
||||
update_label()
|
||||
|
||||
current_label_label = QLabel()
|
||||
update_label()
|
||||
change_label_button = QPushButton("Modify")
|
||||
change_label_button.clicked.connect(modify_label)
|
||||
layout.addWidget(current_label_label,3,0)
|
||||
layout.addWidget(change_label_button,3,1)
|
||||
|
||||
if d.exec_():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class TrezorWallet(NewWallet):
|
||||
wallet_type = 'trezor'
|
||||
@@ -138,7 +179,7 @@ class TrezorWallet(NewWallet):
|
||||
|
||||
def address_id(self, address):
|
||||
account_id, (change, address_index) = self.get_address_index(address)
|
||||
return "%s/%d/%d" % (account_id, change, address_index)
|
||||
return "44'/0'/%s'/%d/%d" % (account_id, change, address_index)
|
||||
|
||||
def create_main_account(self, password):
|
||||
self.create_account('Main account', None) #name, empty password
|
||||
@@ -167,12 +208,9 @@ class TrezorWallet(NewWallet):
|
||||
pass
|
||||
|
||||
def decrypt_message(self, pubkey, message, password):
|
||||
try:
|
||||
address = public_key_to_bc_address(pubkey.decode('hex'))
|
||||
address_path = self.address_id(address)
|
||||
address_n = self.get_client().expand_path(address_path)
|
||||
except Exception, e:
|
||||
raise e
|
||||
address = public_key_to_bc_address(pubkey.decode('hex'))
|
||||
address_path = self.address_id(address)
|
||||
address_n = self.get_client().expand_path(address_path)
|
||||
try:
|
||||
decrypted_msg = self.get_client().decrypt_message(address_n, b64decode(message))
|
||||
except Exception, e:
|
||||
@@ -182,6 +220,8 @@ class TrezorWallet(NewWallet):
|
||||
return str(decrypted_msg)
|
||||
|
||||
def sign_message(self, address, message, password):
|
||||
if not self.check_proper_device():
|
||||
give_error('Wrong device or password')
|
||||
try:
|
||||
address_path = self.address_id(address)
|
||||
address_n = self.get_client().expand_path(address_path)
|
||||
|
||||
Reference in New Issue
Block a user