wizard: show passphrase in the same window as the seed
This commit is contained in:
parent
5e90b3a42d
commit
99a3250b3f
|
@ -385,7 +385,6 @@ Builder.load_string('''
|
||||||
|
|
||||||
|
|
||||||
<ShowSeedDialog>
|
<ShowSeedDialog>
|
||||||
message: ''
|
|
||||||
spacing: '12dp'
|
spacing: '12dp'
|
||||||
value: 'next'
|
value: 'next'
|
||||||
BigLabel:
|
BigLabel:
|
||||||
|
@ -398,10 +397,29 @@ Builder.load_string('''
|
||||||
height: self.minimum_height
|
height: self.minimum_height
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
spacing: '12dp'
|
spacing: '12dp'
|
||||||
SeedButton:
|
|
||||||
text: root.seed_text
|
|
||||||
SeedLabel:
|
SeedLabel:
|
||||||
text: root.message
|
text: root.message
|
||||||
|
SeedButton:
|
||||||
|
text: root.seed_text
|
||||||
|
|
||||||
|
<PassphraseDialog>
|
||||||
|
|
||||||
|
BigLabel:
|
||||||
|
text: "SEED PASSPHRASE"
|
||||||
|
SeedLabel:
|
||||||
|
text: root.passphrase_message
|
||||||
|
GridLayout:
|
||||||
|
cols: 2
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '27dp'
|
||||||
|
BigLabel:
|
||||||
|
text: _('Passphrase')
|
||||||
|
TextInput:
|
||||||
|
id: passphrase_input
|
||||||
|
multiline: False
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '27dp'
|
||||||
|
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
@ -492,11 +510,27 @@ class WizardChoiceDialog(WizardDialog):
|
||||||
def get_params(self, button):
|
def get_params(self, button):
|
||||||
return (button.action,)
|
return (button.action,)
|
||||||
|
|
||||||
class ShowSeedDialog(WizardDialog):
|
|
||||||
|
|
||||||
|
|
||||||
|
class PassphraseDialog(WizardDialog):
|
||||||
|
passphrase = StringProperty('')
|
||||||
|
passphrase_message = ' '.join([
|
||||||
|
_("You may extend your seed with a derivation passphrase."),
|
||||||
|
'\n\n',
|
||||||
|
_("Note: This is NOT your encryption password."),
|
||||||
|
_("Leave this field empty if you are not sure about what it is."),
|
||||||
|
])
|
||||||
|
|
||||||
|
def __init__(self, wizard, **kwargs):
|
||||||
|
WizardDialog.__init__(self, wizard, **kwargs)
|
||||||
|
self.ids.next.disabled = False
|
||||||
|
|
||||||
|
def get_params(self, b):
|
||||||
|
return (self.ids.passphrase_input.text,)
|
||||||
|
|
||||||
|
class ShowSeedDialog(WizardDialog):
|
||||||
seed_text = StringProperty('')
|
seed_text = StringProperty('')
|
||||||
message = _("If you forget your PIN or lose your device, your seed phrase will be the "
|
message = _("If you forget your PIN or lose your device, your seed phrase will be the only way to recover your funds.")
|
||||||
"only way to recover your funds.")
|
|
||||||
|
|
||||||
def on_parent(self, instance, value):
|
def on_parent(self, instance, value):
|
||||||
if value:
|
if value:
|
||||||
|
@ -504,7 +538,7 @@ class ShowSeedDialog(WizardDialog):
|
||||||
self._back = _back = partial(self.ids.back.dispatch, 'on_release')
|
self._back = _back = partial(self.ids.back.dispatch, 'on_release')
|
||||||
|
|
||||||
def get_params(self, b):
|
def get_params(self, b):
|
||||||
return(self.seed_text,)
|
return(self.seed_text, '')
|
||||||
|
|
||||||
|
|
||||||
class WordButton(Button):
|
class WordButton(Button):
|
||||||
|
@ -518,7 +552,7 @@ class RestoreSeedDialog(WizardDialog):
|
||||||
|
|
||||||
def __init__(self, wizard, **kwargs):
|
def __init__(self, wizard, **kwargs):
|
||||||
super(RestoreSeedDialog, self).__init__(wizard, **kwargs)
|
super(RestoreSeedDialog, self).__init__(wizard, **kwargs)
|
||||||
self._test = kwargs['is_valid']
|
self._test = kwargs['is_seed']
|
||||||
from electrum.mnemonic import Mnemonic
|
from electrum.mnemonic import Mnemonic
|
||||||
from electrum.old_mnemonic import words as old_wordlist
|
from electrum.old_mnemonic import words as old_wordlist
|
||||||
self.words = set(Mnemonic('en').wordlist).union(set(old_wordlist))
|
self.words = set(Mnemonic('en').wordlist).union(set(old_wordlist))
|
||||||
|
@ -581,7 +615,7 @@ class RestoreSeedDialog(WizardDialog):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def get_params(self, b):
|
def get_params(self, b):
|
||||||
return (self.get_text(), False, False)
|
return (self.get_text(), '', False)
|
||||||
|
|
||||||
def update_text(self, c):
|
def update_text(self, c):
|
||||||
c = c.lower()
|
c = c.lower()
|
||||||
|
@ -671,7 +705,6 @@ class AddXpubDialog(WizardDialog):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InstallWizard(BaseWizard, Widget):
|
class InstallWizard(BaseWizard, Widget):
|
||||||
'''
|
'''
|
||||||
events::
|
events::
|
||||||
|
@ -711,6 +744,7 @@ class InstallWizard(BaseWizard, Widget):
|
||||||
def choice_dialog(self, **kwargs): WizardChoiceDialog(self, **kwargs).open()
|
def choice_dialog(self, **kwargs): WizardChoiceDialog(self, **kwargs).open()
|
||||||
def multisig_dialog(self, **kwargs): WizardMultisigDialog(self, **kwargs).open()
|
def multisig_dialog(self, **kwargs): WizardMultisigDialog(self, **kwargs).open()
|
||||||
def show_seed_dialog(self, **kwargs): ShowSeedDialog(self, **kwargs).open()
|
def show_seed_dialog(self, **kwargs): ShowSeedDialog(self, **kwargs).open()
|
||||||
|
def passphrase_dialog(self, **kwargs): PassphraseDialog(self, **kwargs).open()
|
||||||
|
|
||||||
def confirm_seed_dialog(self, **kwargs):
|
def confirm_seed_dialog(self, **kwargs):
|
||||||
kwargs['title'] = _('Confirm Seed')
|
kwargs['title'] = _('Confirm Seed')
|
||||||
|
|
|
@ -12,10 +12,10 @@ from electrum.util import UserCancelled
|
||||||
from electrum.base_wizard import BaseWizard
|
from electrum.base_wizard import BaseWizard
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
|
|
||||||
from seed_dialog import SeedDisplayLayout, SeedWarningLayout, SeedInputLayout
|
from seed_dialog import SeedDisplayLayout, CreateSeedLayout, SeedInputLayout, TextInputLayout
|
||||||
from network_dialog import NetworkChoiceLayout
|
from network_dialog import NetworkChoiceLayout
|
||||||
from util import *
|
from util import *
|
||||||
from password_dialog import PasswordLayout, PW_NEW, PW_PASSPHRASE
|
from password_dialog import PasswordLayout, PW_NEW
|
||||||
|
|
||||||
|
|
||||||
class GoBack(Exception):
|
class GoBack(Exception):
|
||||||
|
@ -28,15 +28,11 @@ MSG_ENTER_SEED_OR_MPK = _("Please enter a seed phrase or a master key (xpub or x
|
||||||
MSG_COSIGNER = _("Please enter the master public key of cosigner #%d:")
|
MSG_COSIGNER = _("Please enter the master public key of cosigner #%d:")
|
||||||
MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\
|
MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\
|
||||||
+ _("Leave this field empty if you want to disable encryption.")
|
+ _("Leave this field empty if you want to disable encryption.")
|
||||||
MSG_PASSPHRASE = \
|
MSG_RESTORE_PASSPHRASE = \
|
||||||
_("Please enter your seed derivation passphrase. "
|
_("Please enter your seed derivation passphrase. "
|
||||||
"Note: this is NOT your encryption password. "
|
"Note: this is NOT your encryption password. "
|
||||||
"Leave this field empty if you did not use one or are unsure.")
|
"Leave this field empty if you did not use one or are unsure.")
|
||||||
|
|
||||||
def clean_text(seed_e):
|
|
||||||
text = unicode(seed_e.toPlainText()).strip()
|
|
||||||
text = ' '.join(text.split())
|
|
||||||
return text
|
|
||||||
|
|
||||||
class CosignWidget(QWidget):
|
class CosignWidget(QWidget):
|
||||||
size = 120
|
size = 120
|
||||||
|
@ -248,31 +244,18 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
def remove_from_recently_open(self, filename):
|
def remove_from_recently_open(self, filename):
|
||||||
self.config.remove_from_recently_open(filename)
|
self.config.remove_from_recently_open(filename)
|
||||||
|
|
||||||
def text_input_layout(self, title, message, is_valid):
|
|
||||||
slayout = SeedInputLayout(title=message)
|
|
||||||
slayout.is_valid = is_valid
|
|
||||||
slayout.sanitized_text = lambda: clean_text(slayout.seed_edit())
|
|
||||||
slayout.set_enabled = lambda: self.next_button.setEnabled(slayout.is_valid(slayout.sanitized_text()))
|
|
||||||
slayout.seed_edit().textChanged.connect(slayout.set_enabled)
|
|
||||||
return slayout
|
|
||||||
|
|
||||||
def text_input(self, title, message, is_valid):
|
def text_input(self, title, message, is_valid):
|
||||||
slayout = self.text_input_layout(title, message, is_valid)
|
slayout = TextInputLayout(self, message, is_valid)
|
||||||
self.set_main_layout(slayout.layout(), title, next_enabled=False)
|
self.set_main_layout(slayout.layout(), title, next_enabled=False)
|
||||||
seed = slayout.sanitized_text()
|
return slayout.get_text()
|
||||||
return seed
|
|
||||||
|
|
||||||
def seed_input(self, title, message, is_valid):
|
def seed_input(self, title, message, is_seed, is_passphrase):
|
||||||
slayout = self.text_input_layout(title, message, is_valid)
|
slayout = SeedInputLayout(self, message, is_seed, is_passphrase)
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
vbox.addLayout(slayout.layout())
|
vbox.addLayout(slayout.layout())
|
||||||
if self.opt_ext or self.opt_bip39:
|
|
||||||
vbox.addStretch(1)
|
|
||||||
vbox.addWidget(QLabel(_('Options')+ ':'))
|
|
||||||
if self.opt_ext:
|
|
||||||
cb_ext = QCheckBox(_('Extend this seed with a passphrase'))
|
|
||||||
vbox.addWidget(cb_ext)
|
|
||||||
if self.opt_bip39:
|
if self.opt_bip39:
|
||||||
|
vbox.addStretch(1)
|
||||||
|
vbox.addWidget(QLabel(_('Options') + ':'))
|
||||||
def f(b):
|
def f(b):
|
||||||
slayout.is_valid = (lambda x: bool(x)) if b else is_valid
|
slayout.is_valid = (lambda x: bool(x)) if b else is_valid
|
||||||
slayout.set_enabled()
|
slayout.set_enabled()
|
||||||
|
@ -280,10 +263,10 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
cb_bip39.toggled.connect(f)
|
cb_bip39.toggled.connect(f)
|
||||||
vbox.addWidget(cb_bip39)
|
vbox.addWidget(cb_bip39)
|
||||||
self.set_main_layout(vbox, title, next_enabled=False)
|
self.set_main_layout(vbox, title, next_enabled=False)
|
||||||
seed = slayout.sanitized_text()
|
seed = slayout.get_seed()
|
||||||
is_ext = cb_ext.isChecked() if self.opt_ext else False
|
passphrase = slayout.get_passphrase()
|
||||||
is_bip39 = cb_bip39.isChecked() if self.opt_bip39 else False
|
is_bip39 = cb_bip39.isChecked() if self.opt_bip39 else False
|
||||||
return seed, is_ext, is_bip39
|
return seed, passphrase, is_bip39
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def restore_keys_dialog(self, title, message, is_valid, run_next):
|
def restore_keys_dialog(self, title, message, is_valid, run_next):
|
||||||
|
@ -299,13 +282,14 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
return self.text_input(title, message, is_valid)
|
return self.text_input(title, message, is_valid)
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def restore_seed_dialog(self, run_next, is_valid):
|
def restore_seed_dialog(self, run_next, is_seed):
|
||||||
title = _('Enter Seed')
|
title = _('Enter Seed')
|
||||||
message = _('Please enter your seed phrase in order to restore your wallet.')
|
message = _('Please enter your seed phrase in order to restore your wallet.')
|
||||||
return self.seed_input(title, message, is_valid)
|
is_passphrase = lambda x: True
|
||||||
|
return self.seed_input(title, message, is_seed, is_passphrase)
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def confirm_seed_dialog(self, run_next, is_valid):
|
def confirm_seed_dialog(self, run_next, is_seed, is_passphrase):
|
||||||
self.app.clipboard().clear()
|
self.app.clipboard().clear()
|
||||||
title = _('Confirm Seed')
|
title = _('Confirm Seed')
|
||||||
message = ' '.join([
|
message = ' '.join([
|
||||||
|
@ -313,29 +297,19 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
_('If you lose your seed, your money will be permanently lost.'),
|
_('If you lose your seed, your money will be permanently lost.'),
|
||||||
_('To make sure that you have properly saved your seed, please retype it here.')
|
_('To make sure that you have properly saved your seed, please retype it here.')
|
||||||
])
|
])
|
||||||
return self.seed_input(title, message, is_valid)
|
return self.seed_input(title, message, is_seed, is_passphrase)
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def show_seed_dialog(self, run_next, seed_text):
|
def show_seed_dialog(self, run_next, seed_text):
|
||||||
slayout = SeedWarningLayout(seed_text)
|
slayout = CreateSeedLayout(seed_text)
|
||||||
self.set_main_layout(slayout.layout())
|
self.set_main_layout(slayout.layout())
|
||||||
return seed_text
|
return seed_text, slayout.passphrase()
|
||||||
|
|
||||||
def pw_layout(self, msg, kind):
|
def pw_layout(self, msg, kind):
|
||||||
playout = PasswordLayout(None, msg, kind, self.next_button)
|
playout = PasswordLayout(None, msg, kind, self.next_button)
|
||||||
self.set_main_layout(playout.layout())
|
self.set_main_layout(playout.layout())
|
||||||
return playout.new_password()
|
return playout.new_password()
|
||||||
|
|
||||||
@wizard_dialog
|
|
||||||
def request_passphrase(self, run_next):
|
|
||||||
"""When restoring a wallet, request the passphrase that was used for
|
|
||||||
the wallet on the given device and confirm it. Should return
|
|
||||||
a unicode string."""
|
|
||||||
phrase = self.pw_layout(MSG_PASSPHRASE, PW_PASSPHRASE)
|
|
||||||
if phrase is None:
|
|
||||||
raise UserCancelled
|
|
||||||
return phrase
|
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def request_password(self, run_next):
|
def request_password(self, run_next):
|
||||||
"""Request the user enter a new password and confirm it. Return
|
"""Request the user enter a new password and confirm it. Return
|
||||||
|
@ -405,13 +379,6 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
self.set_main_layout(vbox, '')
|
self.set_main_layout(vbox, '')
|
||||||
return clayout.selected_index()
|
return clayout.selected_index()
|
||||||
|
|
||||||
def get_passphrase(self, msg, confirm):
|
|
||||||
phrase = self.pw_layout(msg, PW_PASSPHRASE)
|
|
||||||
if phrase is None:
|
|
||||||
raise UserCancelled
|
|
||||||
return phrase
|
|
||||||
|
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def account_id_dialog(self, run_next):
|
def account_id_dialog(self, run_next):
|
||||||
message = '\n'.join([
|
message = '\n'.join([
|
||||||
|
|
|
@ -1696,13 +1696,15 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||||
if not self.wallet.has_seed():
|
if not self.wallet.has_seed():
|
||||||
self.show_message(_('This wallet has no seed'))
|
self.show_message(_('This wallet has no seed'))
|
||||||
return
|
return
|
||||||
|
keystore = self.wallet.get_keystore()
|
||||||
try:
|
try:
|
||||||
mnemonic = self.wallet.get_mnemonic(password)
|
mnemonic = keystore.get_mnemonic(password)
|
||||||
|
passphrase = keystore.get_passphrase(password)
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
self.show_error(str(e))
|
self.show_error(str(e))
|
||||||
return
|
return
|
||||||
from seed_dialog import SeedDialog
|
from seed_dialog import SeedDialog
|
||||||
d = SeedDialog(self, mnemonic)
|
d = SeedDialog(self, mnemonic, passphrase)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,12 +47,12 @@ def check_password_strength(password):
|
||||||
return password_strength[min(3, int(score))]
|
return password_strength[min(3, int(score))]
|
||||||
|
|
||||||
|
|
||||||
PW_NEW, PW_CHANGE, PW_PASSPHRASE = range(0, 3)
|
PW_NEW, PW_CHANGE = range(0, 2)
|
||||||
|
|
||||||
|
|
||||||
class PasswordLayout(object):
|
class PasswordLayout(object):
|
||||||
|
|
||||||
titles = [_("Enter Password"), _("Change Password"), _("Enter Passphrase")]
|
titles = [_("Enter Password"), _("Change Password")]
|
||||||
|
|
||||||
def __init__(self, wallet, msg, kind, OK_button):
|
def __init__(self, wallet, msg, kind, OK_button):
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
|
@ -60,8 +60,8 @@ class PasswordLayout(object):
|
||||||
self.pw = QLineEdit()
|
self.pw = QLineEdit()
|
||||||
self.pw.setEchoMode(2)
|
self.pw.setEchoMode(2)
|
||||||
self.new_pw = QLineEdit()
|
self.new_pw = QLineEdit()
|
||||||
self.new_pw.setEchoMode(2)
|
|
||||||
self.conf_pw = QLineEdit()
|
self.conf_pw = QLineEdit()
|
||||||
|
self.new_pw.setEchoMode(2)
|
||||||
self.conf_pw.setEchoMode(2)
|
self.conf_pw.setEchoMode(2)
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.OK_button = OK_button
|
self.OK_button = OK_button
|
||||||
|
@ -76,31 +76,24 @@ class PasswordLayout(object):
|
||||||
grid.setColumnMinimumWidth(1, 100)
|
grid.setColumnMinimumWidth(1, 100)
|
||||||
grid.setColumnStretch(1,1)
|
grid.setColumnStretch(1,1)
|
||||||
|
|
||||||
if kind == PW_PASSPHRASE:
|
logo_grid = QGridLayout()
|
||||||
vbox.addWidget(label)
|
logo_grid.setSpacing(8)
|
||||||
msgs = [_('Passphrase:'), _('Confirm Passphrase:')]
|
logo_grid.setColumnMinimumWidth(0, 70)
|
||||||
|
logo_grid.setColumnStretch(1,1)
|
||||||
|
logo = QLabel()
|
||||||
|
logo.setAlignment(Qt.AlignCenter)
|
||||||
|
logo_grid.addWidget(logo, 0, 0)
|
||||||
|
logo_grid.addWidget(label, 0, 1, 1, 2)
|
||||||
|
vbox.addLayout(logo_grid)
|
||||||
|
m1 = _('New Password:') if kind == PW_NEW else _('Password:')
|
||||||
|
msgs = [m1, _('Confirm Password:')]
|
||||||
|
if wallet and wallet.has_password():
|
||||||
|
grid.addWidget(QLabel(_('Current Password:')), 0, 0)
|
||||||
|
grid.addWidget(self.pw, 0, 1)
|
||||||
|
lockfile = ":icons/lock.png"
|
||||||
else:
|
else:
|
||||||
logo_grid = QGridLayout()
|
lockfile = ":icons/unlock.png"
|
||||||
logo_grid.setSpacing(8)
|
logo.setPixmap(QPixmap(lockfile).scaledToWidth(36))
|
||||||
logo_grid.setColumnMinimumWidth(0, 70)
|
|
||||||
logo_grid.setColumnStretch(1,1)
|
|
||||||
|
|
||||||
logo = QLabel()
|
|
||||||
logo.setAlignment(Qt.AlignCenter)
|
|
||||||
|
|
||||||
logo_grid.addWidget(logo, 0, 0)
|
|
||||||
logo_grid.addWidget(label, 0, 1, 1, 2)
|
|
||||||
vbox.addLayout(logo_grid)
|
|
||||||
|
|
||||||
m1 = _('New Password:') if kind == PW_NEW else _('Password:')
|
|
||||||
msgs = [m1, _('Confirm Password:')]
|
|
||||||
if wallet and wallet.has_password():
|
|
||||||
grid.addWidget(QLabel(_('Current Password:')), 0, 0)
|
|
||||||
grid.addWidget(self.pw, 0, 1)
|
|
||||||
lockfile = ":icons/lock.png"
|
|
||||||
else:
|
|
||||||
lockfile = ":icons/unlock.png"
|
|
||||||
logo.setPixmap(QPixmap(lockfile).scaledToWidth(36))
|
|
||||||
|
|
||||||
grid.addWidget(QLabel(msgs[0]), 1, 0)
|
grid.addWidget(QLabel(msgs[0]), 1, 0)
|
||||||
grid.addWidget(self.new_pw, 1, 1)
|
grid.addWidget(self.new_pw, 1, 1)
|
||||||
|
@ -110,10 +103,9 @@ class PasswordLayout(object):
|
||||||
vbox.addLayout(grid)
|
vbox.addLayout(grid)
|
||||||
|
|
||||||
# Password Strength Label
|
# Password Strength Label
|
||||||
if kind != PW_PASSPHRASE:
|
self.pw_strength = QLabel()
|
||||||
self.pw_strength = QLabel()
|
grid.addWidget(self.pw_strength, 3, 0, 1, 2)
|
||||||
grid.addWidget(self.pw_strength, 3, 0, 1, 2)
|
self.new_pw.textChanged.connect(self.pw_changed)
|
||||||
self.new_pw.textChanged.connect(self.pw_changed)
|
|
||||||
|
|
||||||
def enable_OK():
|
def enable_OK():
|
||||||
OK_button.setEnabled(self.new_pw.text() == self.conf_pw.text())
|
OK_button.setEnabled(self.new_pw.text() == self.conf_pw.text())
|
||||||
|
@ -147,8 +139,7 @@ class PasswordLayout(object):
|
||||||
|
|
||||||
def new_password(self):
|
def new_password(self):
|
||||||
pw = unicode(self.new_pw.text())
|
pw = unicode(self.new_pw.text())
|
||||||
# Empty passphrases are fine and returned empty.
|
if pw == "":
|
||||||
if pw == "" and self.kind != PW_PASSPHRASE:
|
|
||||||
pw = None
|
pw = None
|
||||||
return pw
|
return pw
|
||||||
|
|
||||||
|
|
|
@ -38,13 +38,6 @@ def icon_filename(sid):
|
||||||
else:
|
else:
|
||||||
return ":icons/seed.png"
|
return ":icons/seed.png"
|
||||||
|
|
||||||
class SeedDialog(WindowModalDialog):
|
|
||||||
def __init__(self, parent, seed):
|
|
||||||
WindowModalDialog.__init__(self, parent, ('Electrum - ' + _('Seed')))
|
|
||||||
self.setMinimumWidth(400)
|
|
||||||
vbox = QVBoxLayout(self)
|
|
||||||
vbox.addLayout(SeedWarningLayout(seed).layout())
|
|
||||||
vbox.addLayout(Buttons(CloseButton(self)))
|
|
||||||
|
|
||||||
class SeedLayoutBase(object):
|
class SeedLayoutBase(object):
|
||||||
def _seed_layout(self, seed=None, title=None, sid=None):
|
def _seed_layout(self, seed=None, title=None, sid=None):
|
||||||
|
@ -75,34 +68,124 @@ class SeedLayoutBase(object):
|
||||||
return self.seed_e
|
return self.seed_e
|
||||||
|
|
||||||
|
|
||||||
class SeedInputLayout(SeedLayoutBase):
|
|
||||||
def __init__(self, title=None, sid=None):
|
|
||||||
self.layout_ = self._seed_layout(title=title, sid=sid)
|
|
||||||
|
|
||||||
|
|
||||||
class SeedDisplayLayout(SeedLayoutBase):
|
class SeedDisplayLayout(SeedLayoutBase):
|
||||||
def __init__(self, seed, title=None, sid=None):
|
def __init__(self, seed, title=None, sid=None):
|
||||||
self.layout_ = self._seed_layout(seed=seed, title=title, sid=sid)
|
self.layout_ = self._seed_layout(seed=seed, title=title, sid=sid)
|
||||||
|
|
||||||
|
|
||||||
class SeedWarningLayout(SeedLayoutBase):
|
|
||||||
def __init__(self, seed, title=None):
|
def seed_warning_msg(seed):
|
||||||
if title is None:
|
return ''.join([
|
||||||
title = _("Your wallet generation seed is:")
|
"<p>",
|
||||||
msg = ''.join([
|
_("Please save these %d words on paper (order is important). "),
|
||||||
"<p>",
|
_("This seed will allow you to recover your wallet in case "
|
||||||
_("Please save these %d words on paper (order is important). "),
|
"of computer failure."),
|
||||||
_("This seed will allow you to recover your wallet in case "
|
"</p>",
|
||||||
"of computer failure."),
|
"<b>" + _("WARNING") + ":</b> ",
|
||||||
"</p>",
|
"<ul>",
|
||||||
"<b>" + _("WARNING") + ":</b> ",
|
"<li>" + _("Never disclose your seed.") + "</li>",
|
||||||
"<ul>",
|
"<li>" + _("Never type it on a website.") + "</li>",
|
||||||
"<li>" + _("Never disclose your seed.") + "</li>",
|
"<li>" + _("Do not send your seed to a printer.") + "</li>",
|
||||||
"<li>" + _("Never type it on a website.") + "</li>",
|
"</ul>"
|
||||||
"<li>" + _("Do not send your seed to a printer.") + "</li>",
|
]) % len(seed.split())
|
||||||
"</ul>"
|
|
||||||
]) % len(seed.split())
|
|
||||||
|
class CreateSeedLayout(SeedLayoutBase):
|
||||||
|
|
||||||
|
def __init__(self, seed):
|
||||||
|
title = _("Your wallet generation seed is:")
|
||||||
|
tooltip = '\n'.join([
|
||||||
|
_('You may extend your seed with a passphrase.'),
|
||||||
|
_('Note tha this is NOT your encryption password.'),
|
||||||
|
_('If you do not know what it is, leave it empty.'),
|
||||||
|
])
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
vbox.addLayout(self._seed_layout(seed=seed, title=title))
|
vbox.addLayout(self._seed_layout(seed=seed, title=title))
|
||||||
|
self.passphrase_e = QLineEdit()
|
||||||
|
self.passphrase_e.setToolTip(tooltip)
|
||||||
|
hbox = QHBoxLayout()
|
||||||
|
hbox.addStretch()
|
||||||
|
label = QLabel(_('Passphrase') + ':')
|
||||||
|
label.setToolTip(tooltip)
|
||||||
|
hbox.addWidget(label)
|
||||||
|
hbox.addWidget(self.passphrase_e)
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
msg = seed_warning_msg(seed)
|
||||||
vbox.addWidget(WWLabel(msg))
|
vbox.addWidget(WWLabel(msg))
|
||||||
self.layout_ = vbox
|
self.layout_ = vbox
|
||||||
|
|
||||||
|
def passphrase(self):
|
||||||
|
return unicode(self.passphrase_e.text()).strip()
|
||||||
|
|
||||||
|
|
||||||
|
class TextInputLayout(SeedLayoutBase):
|
||||||
|
|
||||||
|
def __init__(self, parent, title, is_valid):
|
||||||
|
self.is_valid = is_valid
|
||||||
|
self.parent = parent
|
||||||
|
self.layout_ = self._seed_layout(title=title)
|
||||||
|
self.seed_e.textChanged.connect(self.on_edit)
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
return clean_text(self.seed_edit())
|
||||||
|
|
||||||
|
def on_edit(self):
|
||||||
|
self.parent.next_button.setEnabled(self.is_valid(self.get_text()))
|
||||||
|
|
||||||
|
|
||||||
|
class SeedInputLayout(SeedLayoutBase):
|
||||||
|
|
||||||
|
def __init__(self, parent, title, is_seed, is_passphrase):
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.addLayout(self._seed_layout(title=title))
|
||||||
|
self.passphrase_e = QLineEdit()
|
||||||
|
hbox = QHBoxLayout()
|
||||||
|
hbox.addStretch()
|
||||||
|
hbox.addWidget(QLabel(_('Passphrase') + ':'))
|
||||||
|
hbox.addWidget(self.passphrase_e)
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
self.layout_ = vbox
|
||||||
|
self.parent = parent
|
||||||
|
self.is_seed = is_seed
|
||||||
|
self.is_passphrase = is_passphrase
|
||||||
|
self.seed_e.textChanged.connect(self.on_edit)
|
||||||
|
self.passphrase_e.textChanged.connect(self.on_edit)
|
||||||
|
|
||||||
|
def get_passphrase(self):
|
||||||
|
return unicode(self.passphrase_e.text()).strip()
|
||||||
|
|
||||||
|
def get_seed(self):
|
||||||
|
return clean_text(self.seed_edit())
|
||||||
|
|
||||||
|
def on_edit(self):
|
||||||
|
self.parent.next_button.setEnabled(self.is_seed(self.get_seed()) and self.is_passphrase(self.get_passphrase()))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ShowSeedLayout(SeedLayoutBase):
|
||||||
|
|
||||||
|
def __init__(self, seed, passphrase):
|
||||||
|
title = _("Your wallet generation seed is:")
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.addLayout(self._seed_layout(seed=seed, title=title))
|
||||||
|
if passphrase:
|
||||||
|
hbox = QHBoxLayout()
|
||||||
|
passphrase_e = QLineEdit()
|
||||||
|
passphrase_e.setText(passphrase)
|
||||||
|
passphrase_e.setReadOnly(True)
|
||||||
|
hbox.addWidget(QLabel('Your seed passphrase is'))
|
||||||
|
hbox.addWidget(passphrase_e)
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
msg = seed_warning_msg(seed)
|
||||||
|
vbox.addWidget(WWLabel(msg))
|
||||||
|
self.layout_ = vbox
|
||||||
|
|
||||||
|
|
||||||
|
class SeedDialog(WindowModalDialog):
|
||||||
|
def __init__(self, parent, seed, passphrase):
|
||||||
|
WindowModalDialog.__init__(self, parent, ('Electrum - ' + _('Seed')))
|
||||||
|
self.setMinimumWidth(400)
|
||||||
|
vbox = QVBoxLayout(self)
|
||||||
|
vbox.addLayout(ShowSeedLayout(seed, passphrase).layout())
|
||||||
|
vbox.addLayout(Buttons(CloseButton(self)))
|
||||||
|
|
|
@ -49,6 +49,12 @@ expiration_values = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def clean_text(seed_e):
|
||||||
|
text = unicode(seed_e.toPlainText()).strip()
|
||||||
|
text = ' '.join(text.split())
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
class Timer(QThread):
|
class Timer(QThread):
|
||||||
stopped = False
|
stopped = False
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class BaseWizard(object):
|
||||||
self.stack = []
|
self.stack = []
|
||||||
self.plugin = None
|
self.plugin = None
|
||||||
self.keystores = []
|
self.keystores = []
|
||||||
|
self.is_kivy = config.get('gui') == 'kivy'
|
||||||
|
|
||||||
def run(self, *args):
|
def run(self, *args):
|
||||||
action = args[0]
|
action = args[0]
|
||||||
|
@ -229,15 +230,15 @@ class BaseWizard(object):
|
||||||
def restore_from_seed(self):
|
def restore_from_seed(self):
|
||||||
self.opt_bip39 = True
|
self.opt_bip39 = True
|
||||||
self.opt_ext = True
|
self.opt_ext = True
|
||||||
self.restore_seed_dialog(run_next=self.on_seed, is_valid=keystore.is_seed)
|
self.restore_seed_dialog(run_next=self.on_seed, is_seed=keystore.is_seed)
|
||||||
|
|
||||||
def on_seed(self, seed, add_passphrase, is_bip39):
|
def on_seed(self, seed, passphrase, is_bip39):
|
||||||
self.is_bip39 = is_bip39
|
self.is_bip39 = is_bip39
|
||||||
f = lambda x: self.run('create_keystore', seed, x)
|
if self.is_kivy:
|
||||||
if add_passphrase:
|
f = lambda x: self.run('create_keystore', seed, x)
|
||||||
self.request_passphrase(run_next=f)
|
self.passphrase_dialog(run_next=f)
|
||||||
else:
|
else:
|
||||||
f('')
|
self.run('create_keystore', seed, passphrase)
|
||||||
|
|
||||||
def create_keystore(self, seed, passphrase):
|
def create_keystore(self, seed, passphrase):
|
||||||
if self.is_bip39:
|
if self.is_bip39:
|
||||||
|
@ -248,7 +249,6 @@ class BaseWizard(object):
|
||||||
self.on_keystore(k)
|
self.on_keystore(k)
|
||||||
|
|
||||||
def on_bip44(self, seed, passphrase, account_id):
|
def on_bip44(self, seed, passphrase, account_id):
|
||||||
import keystore
|
|
||||||
k = keystore.BIP32_KeyStore({})
|
k = keystore.BIP32_KeyStore({})
|
||||||
bip32_seed = keystore.bip39_to_seed(seed, passphrase)
|
bip32_seed = keystore.bip39_to_seed(seed, passphrase)
|
||||||
derivation = "m/44'/0'/%d'"%account_id
|
derivation = "m/44'/0'/%d'"%account_id
|
||||||
|
@ -311,8 +311,8 @@ class BaseWizard(object):
|
||||||
self.opt_ext = True
|
self.opt_ext = True
|
||||||
self.show_seed_dialog(run_next=self.confirm_seed, seed_text=seed)
|
self.show_seed_dialog(run_next=self.confirm_seed, seed_text=seed)
|
||||||
|
|
||||||
def confirm_seed(self, seed):
|
def confirm_seed(self, seed, passphrase):
|
||||||
self.confirm_seed_dialog(run_next=self.on_seed, is_valid=lambda x: x==seed)
|
self.confirm_seed_dialog(run_next=self.on_seed, is_seed = lambda x: x==seed, is_passphrase=lambda x: x==passphrase)
|
||||||
|
|
||||||
def create_addresses(self):
|
def create_addresses(self):
|
||||||
def task():
|
def task():
|
||||||
|
|
|
@ -209,7 +209,7 @@ class Deterministic_KeyStore(Software_KeyStore):
|
||||||
return pw_decode(self.seed, password).encode('utf8')
|
return pw_decode(self.seed, password).encode('utf8')
|
||||||
|
|
||||||
def get_passphrase(self, password):
|
def get_passphrase(self, password):
|
||||||
return pw_decode(self.passphrase, password).encode('utf8')
|
return pw_decode(self.passphrase, password).encode('utf8') if self.passphrase else ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue