2017-01-22 10:25:24 -08:00
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import print_function
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
import six
|
2016-02-06 02:51:39 -08:00
|
|
|
import sys
|
2016-07-01 23:58:56 -07:00
|
|
|
import os
|
2015-06-26 05:29:26 -07:00
|
|
|
|
2013-08-22 03:39:41 -07:00
|
|
|
from PyQt4.QtGui import *
|
|
|
|
from PyQt4.QtCore import *
|
|
|
|
import PyQt4.QtCore as QtCore
|
|
|
|
|
2014-08-31 06:33:20 -07:00
|
|
|
import electrum
|
2017-03-05 23:33:35 -08:00
|
|
|
from electrum import Wallet, WalletStorage
|
2017-03-05 04:30:57 -08:00
|
|
|
from electrum.util import UserCancelled, InvalidPassword
|
2016-06-20 07:25:11 -07:00
|
|
|
from electrum.base_wizard import BaseWizard
|
2013-09-11 04:55:49 -07:00
|
|
|
from electrum.i18n import _
|
2013-08-29 07:07:55 -07:00
|
|
|
|
2017-01-22 10:25:24 -08:00
|
|
|
from .seed_dialog import SeedLayout, KeysLayout
|
|
|
|
from .network_dialog import NetworkChoiceLayout
|
|
|
|
from .util import *
|
|
|
|
from .password_dialog import PasswordLayout, PW_NEW
|
2013-08-29 07:07:55 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
|
|
|
|
class GoBack(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
MSG_GENERATING_WAIT = _("Electrum is generating your addresses, please wait...")
|
|
|
|
MSG_ENTER_ANYTHING = _("Please enter a seed phrase, a master key, a list of "
|
|
|
|
"Bitcoin addresses, or a list of private keys")
|
|
|
|
MSG_ENTER_SEED_OR_MPK = _("Please enter a seed phrase or a master key (xpub or xprv):")
|
|
|
|
MSG_COSIGNER = _("Please enter the master public key of cosigner #%d:")
|
2016-08-01 09:16:22 -07:00
|
|
|
MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\
|
|
|
|
+ _("Leave this field empty if you want to disable encryption.")
|
2016-08-29 06:33:16 -07:00
|
|
|
MSG_RESTORE_PASSPHRASE = \
|
2016-08-25 00:48:11 -07:00
|
|
|
_("Please enter your seed derivation passphrase. "
|
|
|
|
"Note: this is NOT your encryption password. "
|
|
|
|
"Leave this field empty if you did not use one or are unsure.")
|
2014-04-29 12:19:42 -07:00
|
|
|
|
2016-01-12 05:31:53 -08:00
|
|
|
|
2015-06-26 05:29:26 -07:00
|
|
|
class CosignWidget(QWidget):
|
|
|
|
size = 120
|
|
|
|
|
|
|
|
def __init__(self, m, n):
|
|
|
|
QWidget.__init__(self)
|
|
|
|
self.R = QRect(0, 0, self.size, self.size)
|
|
|
|
self.setGeometry(self.R)
|
2016-01-13 02:20:58 -08:00
|
|
|
self.setMinimumHeight(self.size)
|
|
|
|
self.setMaximumHeight(self.size)
|
2015-06-26 05:29:26 -07:00
|
|
|
self.m = m
|
|
|
|
self.n = n
|
|
|
|
|
|
|
|
def set_n(self, n):
|
|
|
|
self.n = n
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
def set_m(self, m):
|
|
|
|
self.m = m
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
def paintEvent(self, event):
|
|
|
|
import math
|
|
|
|
bgcolor = self.palette().color(QPalette.Background)
|
|
|
|
pen = QPen(bgcolor, 7, QtCore.Qt.SolidLine)
|
|
|
|
qp = QPainter()
|
|
|
|
qp.begin(self)
|
|
|
|
qp.setPen(pen)
|
|
|
|
qp.setRenderHint(QPainter.Antialiasing)
|
|
|
|
qp.setBrush(Qt.gray)
|
|
|
|
for i in range(self.n):
|
|
|
|
alpha = int(16* 360 * i/self.n)
|
|
|
|
alpha2 = int(16* 360 * 1/self.n)
|
|
|
|
qp.setBrush(Qt.green if i<self.m else Qt.gray)
|
|
|
|
qp.drawPie(self.R, alpha, alpha2)
|
|
|
|
qp.end()
|
|
|
|
|
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
|
|
|
|
def wizard_dialog(func):
|
|
|
|
def func_wrapper(*args, **kwargs):
|
|
|
|
run_next = kwargs['run_next']
|
|
|
|
wizard = args[0]
|
|
|
|
wizard.back_button.setText(_('Back') if wizard.can_go_back() else _('Cancel'))
|
|
|
|
try:
|
|
|
|
out = func(*args, **kwargs)
|
|
|
|
except GoBack:
|
2016-10-16 23:27:06 -07:00
|
|
|
wizard.go_back() if wizard.can_go_back() else wizard.close()
|
2016-06-20 07:25:11 -07:00
|
|
|
return
|
|
|
|
except UserCancelled:
|
|
|
|
return
|
|
|
|
#if out is None:
|
|
|
|
# out = ()
|
|
|
|
if type(out) is not tuple:
|
|
|
|
out = (out,)
|
2017-07-31 20:22:18 -07:00
|
|
|
run_next(*out)
|
2016-06-20 07:25:11 -07:00
|
|
|
return func_wrapper
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-02 18:18:20 -08:00
|
|
|
# WindowModalDialog must come first as it overrides show_error
|
2016-06-20 07:25:11 -07:00
|
|
|
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|
|
|
|
2016-10-14 05:05:24 -07:00
|
|
|
def __init__(self, config, app, plugins, storage):
|
|
|
|
BaseWizard.__init__(self, config, storage)
|
2016-01-27 05:10:34 -08:00
|
|
|
QDialog.__init__(self, None)
|
|
|
|
self.setWindowTitle('Electrum - ' + _('Install Wizard'))
|
2015-12-30 18:36:33 -08:00
|
|
|
self.app = app
|
|
|
|
self.config = config
|
|
|
|
# Set for base base class
|
|
|
|
self.plugins = plugins
|
|
|
|
self.language_for_seed = config.get('language')
|
2016-08-28 00:43:22 -07:00
|
|
|
self.setMinimumSize(600, 400)
|
2013-09-12 10:42:00 -07:00
|
|
|
self.connect(self, QtCore.SIGNAL('accept'), self.accept)
|
2016-08-01 09:16:22 -07:00
|
|
|
self.title = QLabel()
|
2016-01-12 05:31:53 -08:00
|
|
|
self.main_widget = QWidget()
|
2016-06-20 07:25:11 -07:00
|
|
|
self.back_button = QPushButton(_("Back"), self)
|
2017-07-12 12:34:14 -07:00
|
|
|
self.back_button.setText(_('Back') if self.can_go_back() else _('Cancel'))
|
2016-01-12 05:31:53 -08:00
|
|
|
self.next_button = QPushButton(_("Next"), self)
|
|
|
|
self.next_button.setDefault(True)
|
|
|
|
self.logo = QLabel()
|
|
|
|
self.please_wait = QLabel(_("Please wait..."))
|
|
|
|
self.please_wait.setAlignment(Qt.AlignCenter)
|
|
|
|
self.icon_filename = None
|
|
|
|
self.loop = QEventLoop()
|
2016-06-20 07:25:11 -07:00
|
|
|
self.rejected.connect(lambda: self.loop.exit(0))
|
|
|
|
self.back_button.clicked.connect(lambda: self.loop.exit(1))
|
|
|
|
self.next_button.clicked.connect(lambda: self.loop.exit(2))
|
2016-01-12 05:31:53 -08:00
|
|
|
outer_vbox = QVBoxLayout(self)
|
|
|
|
inner_vbox = QVBoxLayout()
|
|
|
|
inner_vbox = QVBoxLayout()
|
|
|
|
inner_vbox.addWidget(self.title)
|
|
|
|
inner_vbox.addWidget(self.main_widget)
|
|
|
|
inner_vbox.addStretch(1)
|
|
|
|
inner_vbox.addWidget(self.please_wait)
|
|
|
|
inner_vbox.addStretch(1)
|
|
|
|
icon_vbox = QVBoxLayout()
|
|
|
|
icon_vbox.addWidget(self.logo)
|
|
|
|
icon_vbox.addStretch(1)
|
|
|
|
hbox = QHBoxLayout()
|
|
|
|
hbox.addLayout(icon_vbox)
|
2016-01-13 02:20:58 -08:00
|
|
|
hbox.addSpacing(5)
|
2016-01-12 05:31:53 -08:00
|
|
|
hbox.addLayout(inner_vbox)
|
|
|
|
hbox.setStretchFactor(inner_vbox, 1)
|
|
|
|
outer_vbox.addLayout(hbox)
|
2016-06-20 07:25:11 -07:00
|
|
|
outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
|
2016-01-12 05:31:53 -08:00
|
|
|
self.set_icon(':icons/electrum.png')
|
2016-01-13 02:49:58 -08:00
|
|
|
self.show()
|
|
|
|
self.raise_()
|
2016-01-13 05:25:40 -08:00
|
|
|
self.refresh_gui() # Need for QT on MacOSX. Lame.
|
2016-01-12 05:31:53 -08:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
def run_and_get_wallet(self):
|
|
|
|
|
2017-06-29 09:23:10 -07:00
|
|
|
vbox = QVBoxLayout()
|
|
|
|
hbox = QHBoxLayout()
|
|
|
|
hbox.addWidget(QLabel(_('Wallet') + ':'))
|
|
|
|
self.name_e = QLineEdit()
|
|
|
|
hbox.addWidget(self.name_e)
|
|
|
|
button = QPushButton(_('Choose...'))
|
|
|
|
hbox.addWidget(button)
|
|
|
|
vbox.addLayout(hbox)
|
|
|
|
|
|
|
|
self.msg_label = QLabel('')
|
|
|
|
vbox.addWidget(self.msg_label)
|
|
|
|
hbox2 = QHBoxLayout()
|
|
|
|
self.pw_e = QLineEdit('', self)
|
|
|
|
self.pw_e.setFixedWidth(150)
|
|
|
|
self.pw_e.setEchoMode(2)
|
|
|
|
self.pw_label = QLabel(_('Password') + ':')
|
|
|
|
hbox2.addWidget(self.pw_label)
|
|
|
|
hbox2.addWidget(self.pw_e)
|
|
|
|
hbox2.addStretch()
|
|
|
|
vbox.addLayout(hbox2)
|
|
|
|
self.set_layout(vbox, title=_('Electrum wallet'))
|
|
|
|
|
2017-07-07 14:26:42 -07:00
|
|
|
wallet_folder = os.path.dirname(self.storage.path)
|
|
|
|
|
2017-06-29 09:23:10 -07:00
|
|
|
def on_choose():
|
2017-08-08 02:12:44 -07:00
|
|
|
path = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder)
|
2017-03-05 04:30:57 -08:00
|
|
|
if path:
|
|
|
|
self.name_e.setText(path)
|
|
|
|
|
2017-07-07 14:26:42 -07:00
|
|
|
def on_filename(filename):
|
2017-03-15 04:13:20 -07:00
|
|
|
path = os.path.join(wallet_folder, filename)
|
2017-06-29 09:23:10 -07:00
|
|
|
try:
|
2017-07-07 14:26:42 -07:00
|
|
|
self.storage = WalletStorage(path)
|
2017-06-29 09:23:10 -07:00
|
|
|
except IOError:
|
|
|
|
self.storage = None
|
|
|
|
if self.storage:
|
|
|
|
if not self.storage.file_exists():
|
|
|
|
msg =_("This file does not exist.") + '\n' \
|
|
|
|
+ _("Press 'Next' to create this wallet, or choose another file.")
|
|
|
|
pw = False
|
|
|
|
elif self.storage.file_exists() and self.storage.is_encrypted():
|
|
|
|
msg = _("This file is encrypted.") + '\n' + _('Enter your password or choose another file.')
|
|
|
|
pw = True
|
|
|
|
else:
|
|
|
|
msg = _("Press 'Next' to open this wallet.")
|
|
|
|
pw = False
|
2017-03-05 04:30:57 -08:00
|
|
|
else:
|
2017-06-29 09:23:10 -07:00
|
|
|
msg = _('Cannot read file')
|
|
|
|
pw = False
|
|
|
|
self.msg_label.setText(msg)
|
|
|
|
if pw:
|
|
|
|
self.pw_label.show()
|
2017-03-05 04:30:57 -08:00
|
|
|
self.pw_e.show()
|
|
|
|
self.pw_e.setFocus()
|
2017-06-29 09:23:10 -07:00
|
|
|
else:
|
|
|
|
self.pw_label.hide()
|
|
|
|
self.pw_e.hide()
|
2017-03-05 04:30:57 -08:00
|
|
|
|
2017-06-29 09:23:10 -07:00
|
|
|
button.clicked.connect(on_choose)
|
|
|
|
self.name_e.textChanged.connect(on_filename)
|
2017-07-14 03:53:35 -07:00
|
|
|
n = os.path.basename(self.storage.path)
|
2017-03-15 04:13:20 -07:00
|
|
|
self.name_e.setText(n)
|
2017-03-05 04:30:57 -08:00
|
|
|
|
2017-06-29 09:23:10 -07:00
|
|
|
while True:
|
2017-03-05 23:33:35 -08:00
|
|
|
if self.storage.file_exists() and not self.storage.is_encrypted():
|
2017-03-05 04:30:57 -08:00
|
|
|
break
|
2017-07-12 12:34:14 -07:00
|
|
|
if self.loop.exec_() != 2: # 2 = next
|
2017-07-24 09:19:52 -07:00
|
|
|
return
|
2017-03-05 23:33:35 -08:00
|
|
|
if not self.storage.file_exists():
|
2017-03-05 04:30:57 -08:00
|
|
|
break
|
2017-03-05 23:33:35 -08:00
|
|
|
if self.storage.file_exists() and self.storage.is_encrypted():
|
2017-08-08 02:12:44 -07:00
|
|
|
password = self.pw_e.text()
|
2017-03-05 04:30:57 -08:00
|
|
|
try:
|
2017-03-05 23:33:35 -08:00
|
|
|
self.storage.decrypt(password)
|
2017-03-05 04:30:57 -08:00
|
|
|
break
|
|
|
|
except InvalidPassword as e:
|
|
|
|
QMessageBox.information(None, _('Error'), str(e), _('OK'))
|
|
|
|
continue
|
|
|
|
except BaseException as e:
|
|
|
|
traceback.print_exc(file=sys.stdout)
|
|
|
|
QMessageBox.information(None, _('Error'), str(e), _('OK'))
|
|
|
|
return
|
|
|
|
|
2016-07-01 23:58:56 -07:00
|
|
|
path = self.storage.path
|
|
|
|
if self.storage.requires_split():
|
|
|
|
self.hide()
|
|
|
|
msg = _("The wallet '%s' contains multiple accounts, which are no longer supported in Electrum 2.7.\n\n"
|
|
|
|
"Do you want to split your wallet into multiple files?"%path)
|
|
|
|
if not self.question(msg):
|
|
|
|
return
|
|
|
|
file_list = '\n'.join(self.storage.split_accounts())
|
2016-10-02 02:46:47 -07:00
|
|
|
msg = _('Your accounts have been moved to') + ':\n' + file_list + '\n\n'+ _('Do you want to delete the old file') + ':\n' + path
|
2016-07-01 23:58:56 -07:00
|
|
|
if self.question(msg):
|
|
|
|
os.remove(path)
|
|
|
|
self.show_warning(_('The file was removed'))
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.storage.requires_upgrade():
|
|
|
|
self.hide()
|
|
|
|
msg = _("The format of your wallet '%s' must be upgraded for Electrum. This change will not be backward compatible"%path)
|
|
|
|
if not self.question(msg):
|
|
|
|
return
|
|
|
|
self.storage.upgrade()
|
|
|
|
self.show_warning(_('Your wallet was upgraded successfully'))
|
|
|
|
self.wallet = Wallet(self.storage)
|
|
|
|
return self.wallet
|
|
|
|
|
|
|
|
action = self.storage.get_action()
|
|
|
|
if action and action != 'new':
|
2016-06-20 07:25:11 -07:00
|
|
|
self.hide()
|
|
|
|
msg = _("The file '%s' contains an incompletely created wallet.\n"
|
|
|
|
"Do you want to complete its creation now?") % path
|
|
|
|
if not self.question(msg):
|
|
|
|
if self.question(_("Do you want to delete '%s'?") % path):
|
|
|
|
os.remove(path)
|
|
|
|
self.show_warning(_('The file was removed'))
|
|
|
|
return
|
|
|
|
self.show()
|
2016-07-01 23:58:56 -07:00
|
|
|
if action:
|
|
|
|
# self.wallet is set in run
|
|
|
|
self.run(action)
|
|
|
|
return self.wallet
|
|
|
|
|
2017-03-05 04:30:57 -08:00
|
|
|
self.wallet = Wallet(self.storage)
|
|
|
|
return self.wallet
|
|
|
|
|
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
|
2016-01-13 03:34:42 -08:00
|
|
|
def finished(self):
|
2016-08-23 07:14:41 -07:00
|
|
|
"""Called in hardware client wrapper, in order to close popups."""
|
|
|
|
return
|
2016-01-13 03:34:42 -08:00
|
|
|
|
2016-01-17 05:03:57 -08:00
|
|
|
def on_error(self, exc_info):
|
2016-02-06 02:51:39 -08:00
|
|
|
if not isinstance(exc_info[1], UserCancelled):
|
2016-01-17 05:03:57 -08:00
|
|
|
traceback.print_exception(*exc_info)
|
|
|
|
self.show_error(str(exc_info[1]))
|
|
|
|
|
2016-01-12 05:31:53 -08:00
|
|
|
def set_icon(self, filename):
|
|
|
|
prior_filename, self.icon_filename = self.icon_filename, filename
|
2016-01-13 05:25:40 -08:00
|
|
|
self.logo.setPixmap(QPixmap(filename).scaledToWidth(60))
|
2016-01-12 05:31:53 -08:00
|
|
|
return prior_filename
|
|
|
|
|
2017-03-17 05:47:27 -07:00
|
|
|
def set_layout(self, layout, title=None, next_enabled=True):
|
2016-08-01 09:16:22 -07:00
|
|
|
self.title.setText("<b>%s</b>"%title if title else "")
|
2016-01-12 06:32:13 -08:00
|
|
|
self.title.setVisible(bool(title))
|
2016-01-13 05:25:40 -08:00
|
|
|
# Get rid of any prior layout by assigning it to a temporary widget
|
2016-01-12 05:31:53 -08:00
|
|
|
prior_layout = self.main_widget.layout()
|
|
|
|
if prior_layout:
|
|
|
|
QWidget().setLayout(prior_layout)
|
|
|
|
self.main_widget.setLayout(layout)
|
2016-06-20 07:25:11 -07:00
|
|
|
self.back_button.setEnabled(True)
|
2016-01-13 05:25:40 -08:00
|
|
|
self.next_button.setEnabled(next_enabled)
|
2016-08-28 01:47:12 -07:00
|
|
|
if next_enabled:
|
|
|
|
self.next_button.setFocus()
|
2016-01-12 05:31:53 -08:00
|
|
|
self.main_widget.setVisible(True)
|
|
|
|
self.please_wait.setVisible(False)
|
2017-03-05 04:30:57 -08:00
|
|
|
|
|
|
|
def exec_layout(self, layout, title=None, raise_on_cancel=True,
|
|
|
|
next_enabled=True):
|
|
|
|
self.set_layout(layout, title, next_enabled)
|
2016-01-13 03:34:42 -08:00
|
|
|
result = self.loop.exec_()
|
|
|
|
if not result and raise_on_cancel:
|
2016-01-12 05:31:53 -08:00
|
|
|
raise UserCancelled
|
2016-06-20 07:25:11 -07:00
|
|
|
if result == 1:
|
|
|
|
raise GoBack
|
2016-01-12 06:32:13 -08:00
|
|
|
self.title.setVisible(False)
|
2016-06-20 07:25:11 -07:00
|
|
|
self.back_button.setEnabled(False)
|
2016-01-12 05:31:53 -08:00
|
|
|
self.next_button.setEnabled(False)
|
|
|
|
self.main_widget.setVisible(False)
|
|
|
|
self.please_wait.setVisible(True)
|
2016-01-12 06:36:25 -08:00
|
|
|
self.refresh_gui()
|
2016-01-13 03:34:42 -08:00
|
|
|
return result
|
2016-01-12 06:36:25 -08:00
|
|
|
|
|
|
|
def refresh_gui(self):
|
2016-01-12 05:31:53 -08:00
|
|
|
# For some reason, to refresh the GUI this needs to be called twice
|
|
|
|
self.app.processEvents()
|
|
|
|
self.app.processEvents()
|
2013-11-03 02:03:45 -08:00
|
|
|
|
2015-12-30 18:36:33 -08:00
|
|
|
def remove_from_recently_open(self, filename):
|
|
|
|
self.config.remove_from_recently_open(filename)
|
|
|
|
|
2016-08-25 00:48:11 -07:00
|
|
|
def text_input(self, title, message, is_valid):
|
2016-10-22 01:06:51 -07:00
|
|
|
slayout = KeysLayout(parent=self, title=message, is_valid=is_valid)
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(slayout, title, next_enabled=False)
|
2016-10-22 01:06:51 -07:00
|
|
|
return slayout.get_text()
|
2016-01-12 05:31:53 -08:00
|
|
|
|
2016-10-12 06:00:10 -07:00
|
|
|
def seed_input(self, title, message, is_seed, options):
|
|
|
|
slayout = SeedLayout(title=message, is_seed=is_seed, options=options, parent=self)
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(slayout, title, next_enabled=False)
|
2016-10-12 06:00:10 -07:00
|
|
|
return slayout.get_seed(), slayout.is_bip39, slayout.is_ext
|
2016-08-25 00:48:11 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
2016-09-26 03:02:54 -07:00
|
|
|
def add_xpub_dialog(self, title, message, is_valid, run_next):
|
2016-06-20 07:25:11 -07:00
|
|
|
return self.text_input(title, message, is_valid)
|
2015-12-30 18:36:33 -08:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
2016-07-01 02:44:26 -07:00
|
|
|
def add_cosigner_dialog(self, run_next, index, is_valid):
|
|
|
|
title = _("Add Cosigner") + " %d"%index
|
|
|
|
message = ' '.join([
|
2016-09-28 08:03:02 -07:00
|
|
|
_('Please enter the master public key (xpub) of your cosigner.'),
|
|
|
|
_('Enter their master private key (xprv) if you want to be able to sign for them.')
|
2016-07-01 02:44:26 -07:00
|
|
|
])
|
|
|
|
return self.text_input(title, message, is_valid)
|
|
|
|
|
|
|
|
@wizard_dialog
|
2016-08-30 00:51:53 -07:00
|
|
|
def restore_seed_dialog(self, run_next, test):
|
2016-12-19 04:25:16 -08:00
|
|
|
options = []
|
|
|
|
if self.opt_ext:
|
|
|
|
options.append('ext')
|
|
|
|
if self.opt_bip39:
|
|
|
|
options.append('bip39')
|
2016-07-01 02:44:26 -07:00
|
|
|
title = _('Enter Seed')
|
|
|
|
message = _('Please enter your seed phrase in order to restore your wallet.')
|
2016-12-19 04:25:16 -08:00
|
|
|
return self.seed_input(title, message, test, options)
|
2016-07-01 02:44:26 -07:00
|
|
|
|
|
|
|
@wizard_dialog
|
2016-08-30 00:51:53 -07:00
|
|
|
def confirm_seed_dialog(self, run_next, test):
|
2015-12-30 18:36:33 -08:00
|
|
|
self.app.clipboard().clear()
|
2016-07-01 02:44:26 -07:00
|
|
|
title = _('Confirm Seed')
|
2016-08-01 09:16:22 -07:00
|
|
|
message = ' '.join([
|
2016-08-30 23:50:31 -07:00
|
|
|
_('Your seed is important!'),
|
2016-08-28 01:33:01 -07:00
|
|
|
_('If you lose your seed, your money will be permanently lost.'),
|
2016-08-01 09:16:22 -07:00
|
|
|
_('To make sure that you have properly saved your seed, please retype it here.')
|
|
|
|
])
|
2016-10-12 06:00:10 -07:00
|
|
|
seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
|
2016-08-30 00:51:53 -07:00
|
|
|
return seed
|
2016-06-20 07:25:11 -07:00
|
|
|
|
|
|
|
@wizard_dialog
|
2016-07-01 02:44:26 -07:00
|
|
|
def show_seed_dialog(self, run_next, seed_text):
|
2016-10-12 06:00:10 -07:00
|
|
|
title = _("Your wallet generation seed is:")
|
|
|
|
slayout = SeedLayout(seed=seed_text, title=title, msg=True, options=['ext'])
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(slayout)
|
2016-10-12 06:00:10 -07:00
|
|
|
return slayout.is_ext
|
2015-12-30 18:36:33 -08:00
|
|
|
|
2016-01-12 06:32:13 -08:00
|
|
|
def pw_layout(self, msg, kind):
|
|
|
|
playout = PasswordLayout(None, msg, kind, self.next_button)
|
2017-02-09 08:08:27 -08:00
|
|
|
playout.encrypt_cb.setChecked(True)
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(playout.layout())
|
2017-02-09 08:08:27 -08:00
|
|
|
return playout.new_password(), playout.encrypt_cb.isChecked()
|
2015-12-30 18:36:33 -08:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
|
|
|
def request_password(self, run_next):
|
2015-12-30 18:36:33 -08:00
|
|
|
"""Request the user enter a new password and confirm it. Return
|
|
|
|
the password or None for no password."""
|
2016-06-20 07:25:11 -07:00
|
|
|
return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW)
|
2015-12-30 18:36:33 -08:00
|
|
|
|
2016-01-09 01:35:10 -08:00
|
|
|
def show_restore(self, wallet, network):
|
2016-01-13 05:25:40 -08:00
|
|
|
# FIXME: these messages are shown after the install wizard is
|
|
|
|
# finished and the window closed. On MacOSX they appear parented
|
|
|
|
# with a re-appeared ghost install wizard window...
|
2016-01-09 01:35:10 -08:00
|
|
|
if network:
|
|
|
|
def task():
|
|
|
|
wallet.wait_until_synchronized()
|
|
|
|
if wallet.is_found():
|
|
|
|
msg = _("Recovery successful")
|
2016-01-05 04:51:03 -08:00
|
|
|
else:
|
2016-01-09 01:35:10 -08:00
|
|
|
msg = _("No transactions found for this seed")
|
|
|
|
self.emit(QtCore.SIGNAL('synchronized'), msg)
|
|
|
|
self.connect(self, QtCore.SIGNAL('synchronized'), self.show_message)
|
|
|
|
t = threading.Thread(target = task)
|
2016-04-05 01:49:28 -07:00
|
|
|
t.daemon = True
|
2016-01-09 01:35:10 -08:00
|
|
|
t.start()
|
|
|
|
else:
|
|
|
|
msg = _("This wallet was restored offline. It may "
|
|
|
|
"contain more addresses than displayed.")
|
|
|
|
self.show_message(msg)
|
2016-01-06 01:31:55 -08:00
|
|
|
|
2016-07-31 01:59:42 -07:00
|
|
|
@wizard_dialog
|
2016-08-23 04:40:11 -07:00
|
|
|
def confirm_dialog(self, title, message, run_next):
|
|
|
|
self.confirm(message, title)
|
2016-07-31 01:59:42 -07:00
|
|
|
|
2016-08-23 04:40:11 -07:00
|
|
|
def confirm(self, message, title):
|
2017-09-18 15:14:12 -07:00
|
|
|
area = QScrollArea()
|
|
|
|
label = WWLabel(message)
|
|
|
|
area.setWidget(label)
|
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
vbox = QVBoxLayout()
|
2017-09-18 15:14:12 -07:00
|
|
|
vbox.addWidget(area)
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(vbox, title)
|
2015-08-26 09:35:21 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
|
|
|
def action_dialog(self, action, run_next):
|
|
|
|
self.run(action)
|
2014-05-09 04:12:07 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
def terminate(self):
|
|
|
|
self.emit(QtCore.SIGNAL('accept'))
|
2016-01-12 06:32:13 -08:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
def waiting_dialog(self, task, msg):
|
|
|
|
self.please_wait.setText(MSG_GENERATING_WAIT)
|
|
|
|
self.refresh_gui()
|
|
|
|
t = threading.Thread(target = task)
|
|
|
|
t.start()
|
2017-01-21 16:01:52 -08:00
|
|
|
t.join()
|
2013-08-22 03:39:41 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
|
|
|
def choice_dialog(self, title, message, choices, run_next):
|
2017-01-30 01:36:56 -08:00
|
|
|
c_values = [x[0] for x in choices]
|
|
|
|
c_titles = [x[1] for x in choices]
|
2016-06-20 07:25:11 -07:00
|
|
|
clayout = ChoicesLayout(message, c_titles)
|
2016-01-22 05:16:58 -08:00
|
|
|
vbox = QVBoxLayout()
|
2016-06-20 07:25:11 -07:00
|
|
|
vbox.addLayout(clayout.layout())
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(vbox, title)
|
2016-06-20 07:25:11 -07:00
|
|
|
action = c_values[clayout.selected_index()]
|
|
|
|
return action
|
2016-01-22 05:16:58 -08:00
|
|
|
|
2016-08-23 01:00:46 -07:00
|
|
|
def query_choice(self, msg, choices):
|
|
|
|
"""called by hardware wallets"""
|
|
|
|
clayout = ChoicesLayout(msg, choices)
|
|
|
|
vbox = QVBoxLayout()
|
|
|
|
vbox.addLayout(clayout.layout())
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(vbox, '')
|
2016-08-23 01:00:46 -07:00
|
|
|
return clayout.selected_index()
|
|
|
|
|
2016-08-14 02:30:38 -07:00
|
|
|
@wizard_dialog
|
2016-09-26 06:39:30 -07:00
|
|
|
def line_dialog(self, run_next, title, message, default, test, warning=''):
|
2016-08-30 00:51:53 -07:00
|
|
|
vbox = QVBoxLayout()
|
|
|
|
vbox.addWidget(WWLabel(message))
|
2016-08-14 02:30:38 -07:00
|
|
|
line = QLineEdit()
|
2016-08-15 02:48:33 -07:00
|
|
|
line.setText(default)
|
2016-08-30 00:51:53 -07:00
|
|
|
def f(text):
|
2017-08-08 02:12:44 -07:00
|
|
|
self.next_button.setEnabled(test(text))
|
2016-08-30 00:51:53 -07:00
|
|
|
line.textEdited.connect(f)
|
2016-08-14 02:30:38 -07:00
|
|
|
vbox.addWidget(line)
|
2016-09-26 03:02:54 -07:00
|
|
|
vbox.addWidget(WWLabel(warning))
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(vbox, title, next_enabled=test(default))
|
2017-01-30 01:36:56 -08:00
|
|
|
return ' '.join(line.text().split())
|
2016-08-14 02:30:38 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
|
|
|
def show_xpub_dialog(self, xpub, run_next):
|
2016-08-01 09:16:22 -07:00
|
|
|
msg = ' '.join([
|
|
|
|
_("Here is your master public key."),
|
|
|
|
_("Please share it with your cosigners.")
|
|
|
|
])
|
2014-05-09 07:27:12 -07:00
|
|
|
vbox = QVBoxLayout()
|
2016-10-12 06:00:10 -07:00
|
|
|
layout = SeedLayout(xpub, title=msg, icon=False)
|
2016-06-20 07:25:11 -07:00
|
|
|
vbox.addLayout(layout.layout())
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(vbox, _('Master Public Key'))
|
2016-06-20 07:25:11 -07:00
|
|
|
return None
|
2013-09-12 10:42:00 -07:00
|
|
|
|
2016-10-14 05:05:24 -07:00
|
|
|
def init_network(self, network):
|
|
|
|
message = _("Electrum communicates with remote servers to get "
|
2016-01-13 03:34:42 -08:00
|
|
|
"information about your transactions and addresses. The "
|
2017-09-16 16:41:03 -07:00
|
|
|
"servers all fulfill the same purpose only differing in "
|
2016-01-13 03:34:42 -08:00
|
|
|
"hardware. In most cases you simply want to let Electrum "
|
|
|
|
"pick one at random. However if you prefer feel free to "
|
|
|
|
"select a server manually.")
|
|
|
|
choices = [_("Auto connect"), _("Select server manually")]
|
2016-10-14 05:05:24 -07:00
|
|
|
title = _("How do you want to connect to a server? ")
|
|
|
|
clayout = ChoicesLayout(message, choices)
|
2016-10-24 05:57:02 -07:00
|
|
|
self.back_button.setText(_('Cancel'))
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(clayout.layout(), title)
|
2016-10-14 05:05:24 -07:00
|
|
|
r = clayout.selected_index()
|
2017-03-29 01:07:42 -07:00
|
|
|
if r == 1:
|
2016-01-13 03:34:42 -08:00
|
|
|
nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
|
2017-03-05 04:30:57 -08:00
|
|
|
if self.exec_layout(nlayout.layout()):
|
2017-03-29 01:07:42 -07:00
|
|
|
nlayout.accept()
|
2016-10-14 05:05:24 -07:00
|
|
|
else:
|
2017-03-29 01:07:42 -07:00
|
|
|
network.auto_connect = True
|
|
|
|
self.config.set_key('auto_connect', True, True)
|
2013-09-03 05:32:56 -07:00
|
|
|
|
2016-06-20 07:25:11 -07:00
|
|
|
@wizard_dialog
|
|
|
|
def multisig_dialog(self, run_next):
|
2015-06-26 05:29:26 -07:00
|
|
|
cw = CosignWidget(2, 2)
|
2016-06-14 02:16:57 -07:00
|
|
|
m_edit = QSlider(Qt.Horizontal, self)
|
|
|
|
n_edit = QSlider(Qt.Horizontal, self)
|
2015-06-26 05:29:26 -07:00
|
|
|
n_edit.setMinimum(2)
|
|
|
|
n_edit.setMaximum(15)
|
|
|
|
m_edit.setMinimum(1)
|
|
|
|
m_edit.setMaximum(2)
|
2016-06-14 02:16:57 -07:00
|
|
|
n_edit.setValue(2)
|
|
|
|
m_edit.setValue(2)
|
|
|
|
n_label = QLabel()
|
|
|
|
m_label = QLabel()
|
|
|
|
grid = QGridLayout()
|
|
|
|
grid.addWidget(n_label, 0, 0)
|
|
|
|
grid.addWidget(n_edit, 0, 1)
|
|
|
|
grid.addWidget(m_label, 1, 0)
|
|
|
|
grid.addWidget(m_edit, 1, 1)
|
|
|
|
def on_m(m):
|
|
|
|
m_label.setText(_('Require %d signatures')%m)
|
|
|
|
cw.set_m(m)
|
|
|
|
def on_n(n):
|
|
|
|
n_label.setText(_('From %d cosigners')%n)
|
|
|
|
cw.set_n(n)
|
|
|
|
m_edit.setMaximum(n)
|
|
|
|
n_edit.valueChanged.connect(on_n)
|
|
|
|
m_edit.valueChanged.connect(on_m)
|
|
|
|
on_n(2)
|
|
|
|
on_m(2)
|
2016-01-13 02:20:58 -08:00
|
|
|
vbox = QVBoxLayout()
|
|
|
|
vbox.addWidget(cw)
|
2016-06-20 07:25:11 -07:00
|
|
|
vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:")))
|
2016-06-14 02:16:57 -07:00
|
|
|
vbox.addLayout(grid)
|
2017-03-05 04:30:57 -08:00
|
|
|
self.exec_layout(vbox, _("Multi-Signature Wallet"))
|
2015-06-26 05:29:26 -07:00
|
|
|
m = int(m_edit.value())
|
|
|
|
n = int(n_edit.value())
|
2016-06-20 07:25:11 -07:00
|
|
|
return (m, n)
|