Example #1
0
def test_password_for_storage_encryption(storage: WalletStorage,
                                         password: str) -> bool:
    try:
        storage.decrypt(password)
    except InvalidPassword:
        return False
    else:
        return True
Example #2
0
    def __init__(self, config: 'SimpleConfig', daemon: 'Daemon',
                 plugins: 'Plugins'):

        self.config = config
        self.network = daemon.network
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists():
            print("Wallet not found. try 'electrum-ltc create'")
            exit()
        if storage.is_encrypted():
            password = getpass.getpass('Password:'******'')
        self.encoding = locale.getpreferredencoding()

        self.stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()
        curses.start_color()
        curses.use_default_colors()
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_CYAN)
        curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
        self.stdscr.keypad(1)
        self.stdscr.border(0)
        self.maxy, self.maxx = self.stdscr.getmaxyx()
        self.set_cursor(0)
        self.w = curses.newwin(10, 50, 5, 5)

        self.tab = 0
        self.pos = 0
        self.popup_pos = 0

        self.str_recipient = ""
        self.str_description = ""
        self.str_amount = ""
        self.str_fee = ""
        self.history = None
        self.txid = []

        util.register_callback(self.update,
                               ['wallet_updated', 'network_updated'])

        self.tab_names = [
            _("History"),
            _("Send"),
            _("Receive"),
            _("Addresses"),
            _("Contacts"),
            _("Banner")
        ]
        self.num_tabs = len(self.tab_names)
Example #3
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    def __init__(self, config, app, plugins):
        QDialog.__init__(self, None)
        BaseWizard.__init__(self, config, plugins)
        self.setWindowTitle('Electrum-LTC  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(_("Back"), self)
        self.back_button.setText(_('Back') if self.can_go_back() else _('Cancel'))
        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()
        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))
        outer_vbox = QVBoxLayout(self)
        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)
        scroll_widget = QWidget()
        scroll_widget.setLayout(inner_vbox)
        scroll = QScrollArea()
        scroll.setWidget(scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        icon_vbox = QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addWidget(scroll)
        hbox.setStretchFactor(scroll, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon('electrum-ltc.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def select_storage(self, path, get_wallet_from_daemon) -> Tuple[str, Optional[WalletStorage]]:

        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-LTC wallet'))

        self.temp_storage = WalletStorage(path, manual_upgrades=True)
        wallet_folder = os.path.dirname(self.temp_storage.path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder)
            if path:
                self.name_e.setText(path)

        def on_filename(filename):
            path = os.path.join(wallet_folder, filename)
            wallet_from_memory = get_wallet_from_daemon(path)
            try:
                if wallet_from_memory:
                    self.temp_storage = wallet_from_memory.storage
                else:
                    self.temp_storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except BaseException:
                self.logger.exception('')
                self.temp_storage = None
                self.next_button.setEnabled(False)
            user_needs_to_enter_password = False
            if self.temp_storage:
                if not self.temp_storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                elif not wallet_from_memory:
                    if self.temp_storage.is_encrypted_with_user_pw():
                        msg = _("This file is encrypted with a password.") + '\n' \
                              + _('Enter your password or choose another file.')
                        user_needs_to_enter_password = True
                    elif self.temp_storage.is_encrypted_with_hw_device():
                        msg = _("This file is encrypted using a hardware device.") + '\n' \
                              + _("Press 'Next' to choose device to decrypt.")
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
            else:
                msg = _('Cannot read file')
            self.msg_label.setText(msg)
            if user_needs_to_enter_password:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        button.clicked.connect(on_choose)
        self.name_e.textChanged.connect(on_filename)
        n = os.path.basename(self.temp_storage.path)
        self.name_e.setText(n)

        while True:
            if self.loop.exec_() != 2:  # 2 = next
                raise UserCancelled
            if self.temp_storage.file_exists() and not self.temp_storage.is_encrypted():
                break
            if not self.temp_storage.file_exists():
                break
            wallet_from_memory = get_wallet_from_daemon(self.temp_storage.path)
            if wallet_from_memory:
                raise WalletAlreadyOpenInMemory(wallet_from_memory)
            if self.temp_storage.file_exists() and self.temp_storage.is_encrypted():
                if self.temp_storage.is_encrypted_with_user_pw():
                    password = self.pw_e.text()
                    try:
                        self.temp_storage.decrypt(password)
                        break
                    except InvalidPassword as e:
                        self.show_message(title=_('Error'), msg=str(e))
                        continue
                    except BaseException as e:
                        self.logger.exception('')
                        self.show_message(title=_('Error'), msg=str(e))
                        raise UserCancelled()
                elif self.temp_storage.is_encrypted_with_hw_device():
                    try:
                        self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET, storage=self.temp_storage)
                    except InvalidPassword as e:
                        self.show_message(title=_('Error'),
                                          msg=_('Failed to decrypt using this hardware device.') + '\n' +
                                              _('If you use a passphrase, make sure it is correct.'))
                        self.reset_stack()
                        return self.select_storage(path, get_wallet_from_daemon)
                    except BaseException as e:
                        self.logger.exception('')
                        self.show_message(title=_('Error'), msg=str(e))
                        raise UserCancelled()
                    if self.temp_storage.is_past_initial_decryption():
                        break
                    else:
                        raise UserCancelled()
                else:
                    raise Exception('Unexpected encryption version')

        return self.temp_storage.path, (self.temp_storage if self.temp_storage.file_exists() else None)  #

    def run_upgrades(self, storage):
        path = storage.path
        if storage.requires_split():
            self.hide()
            msg = _("The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n"
                    "Do you want to split your wallet into multiple files?").format(path)
            if not self.question(msg):
                return
            file_list = '\n'.join(storage.split_accounts())
            msg = _('Your accounts have been moved to') + ':\n' + file_list + '\n\n'+ _('Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            # raise now, to avoid having the old storage opened
            raise UserCancelled()

        action = storage.get_action()
        if action and storage.requires_upgrade():
            raise WalletFileException('Incomplete wallet files cannot be upgraded.')
        if action:
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(_("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
            self.data = storage.db.data # FIXME
            self.run(action)
            for k, v in self.data.items():
                storage.put(k, v)
            storage.write()
            return

        if storage.requires_upgrade():
            self.upgrade_storage(storage)

    def finished(self):
        """Called in hardware client wrapper, in order to close popups."""
        return

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            self.logger.error("on_error", exc_info=exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(QPixmap(icon_path(filename))
                            .scaledToWidth(60, mode=Qt.SmoothTransformation))
        return prior_filename

    def set_layout(self, layout, title=None, next_enabled=True):
        self.title.setText("<b>%s</b>"%title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)

    def exec_layout(self, layout, title=None, raise_on_cancel=True,
                        next_enabled=True):
        self.set_layout(layout, title, next_enabled)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack from None
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid, allow_multi=False):
        slayout = KeysLayout(parent=self, header_layout=message, is_valid=is_valid,
                             allow_multi=allow_multi)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_text()

    def seed_input(self, title, message, is_seed, options):
        slayout = SeedLayout(title=message, is_seed=is_seed, options=options, parent=self)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    @wizard_dialog
    def add_xpub_dialog(self, title, message, is_valid, run_next, allow_multi=False, show_wif_help=False):
        header_layout = QHBoxLayout()
        label = WWLabel(message)
        label.setMinimumWidth(400)
        header_layout.addWidget(label)
        if show_wif_help:
            header_layout.addWidget(InfoButton(WIF_HELP_TEXT), alignment=Qt.AlignRight)
        return self.text_input(title, header_layout, is_valid, allow_multi)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d"%index
        message = ' '.join([
            _('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.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, test):
        options = []
        if self.opt_ext:
            options.append('ext')
        if self.opt_bip39:
            options.append('bip39')
        title = _('Enter Seed')
        message = _('Please enter your seed phrase in order to restore your wallet.')
        return self.seed_input(title, message, test, options)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, test):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('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.')
        ])
        seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
        return seed

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        title =  _("Your wallet generation seed is:")
        slayout = SeedLayout(seed=seed_text, title=title, msg=True, options=['ext'])
        self.exec_layout(slayout)
        return slayout.is_ext

    def pw_layout(self, msg, kind, force_disable_encrypt_cb):
        playout = PasswordLayout(msg=msg, kind=kind, OK_button=self.next_button,
                                 force_disable_encrypt_cb=force_disable_encrypt_cb)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.new_password(), playout.encrypt_cb.isChecked()

    @wizard_dialog
    def request_password(self, run_next, force_disable_encrypt_cb=False):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW, force_disable_encrypt_cb)

    @wizard_dialog
    def request_storage_encryption(self, run_next):
        playout = PasswordLayoutForHW(MSG_HW_STORAGE_ENCRYPTION)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.encrypt_cb.isChecked()

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next):
        self.confirm(message, title)

    def confirm(self, message, title):
        label = WWLabel(message)
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        self.exec_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self, **kwargs):
        self.accept_signal.emit()

    def waiting_dialog(self, task, msg, on_finished=None):
        label = WWLabel(msg)
        vbox = QVBoxLayout()
        vbox.addSpacing(100)
        label.setMinimumWidth(300)
        label.setAlignment(Qt.AlignCenter)
        vbox.addWidget(label)
        self.set_layout(vbox, next_enabled=False)
        self.back_button.setEnabled(False)

        t = threading.Thread(target=task)
        t.start()
        while True:
            t.join(1.0/60)
            if t.is_alive():
                self.refresh_gui()
            else:
                break
        if on_finished:
            on_finished()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def choice_and_line_dialog(self, title: str, message1: str, choices: List[Tuple[str, str, str]],
                               message2: str, test_text: Callable[[str], int],
                               run_next, default_choice_idx: int=0) -> Tuple[str, str]:
        vbox = QVBoxLayout()

        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        c_default_text = [x[2] for x in choices]
        def on_choice_click(clayout):
            idx = clayout.selected_index()
            line.setText(c_default_text[idx])
        clayout = ChoicesLayout(message1, c_titles, on_choice_click,
                                checked_index=default_choice_idx)
        vbox.addLayout(clayout.layout())

        vbox.addSpacing(50)
        vbox.addWidget(WWLabel(message2))

        line = QLineEdit()
        def on_text_change(text):
            self.next_button.setEnabled(test_text(text))
        line.textEdited.connect(on_text_change)
        on_choice_click(clayout)  # set default text for "line"
        vbox.addWidget(line)

        self.exec_layout(vbox, title)
        choice = c_values[clayout.selected_index()]
        return str(line.text()), choice

    @wizard_dialog
    def line_dialog(self, run_next, title, message, default, test, warning='',
                    presets=(), warn_issue4566=False):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
            if warn_issue4566:
                text_whitespace_normalised = ' '.join(text.split())
                warn_issue4566_label.setVisible(text != text_whitespace_normalised)
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        warn_issue4566_label = WWLabel(MSG_PASSPHRASE_WARN_ISSUE4566)
        warn_issue4566_label.setVisible(False)
        vbox.addWidget(warn_issue4566_label)

        for preset in presets:
            button = QPushButton(preset[0])
            button.clicked.connect(lambda __, text=preset[1]: line.setText(text))
            button.setMinimumWidth(150)
            hbox = QHBoxLayout()
            hbox.addWidget(button, alignment=Qt.AlignCenter)
            vbox.addLayout(hbox)

        self.exec_layout(vbox, title, next_enabled=test(default))
        return line.text()

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedLayout(xpub, title=msg, icon=False, for_seed_words=False)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _("Electrum communicates with remote servers to get "
                  "information about your transactions and addresses. The "
                  "servers all fulfill the same purpose only differing in "
                  "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")]
        title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(message, choices)
        self.back_button.setText(_('Cancel'))
        self.exec_layout(clayout.layout(), title)
        r = clayout.selected_index()
        if r == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()
        else:
            network.auto_connect = True
            self.config.set_key('auto_connect', True, True)

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        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 {0} signatures').format(m))
            cw.set_m(m)
        def on_n(n):
            n_label.setText(_('From {0} cosigners').format(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)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:")))
        vbox.addLayout(grid)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)
Example #4
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    def __init__(self, config, app, plugins, storage):
        BaseWizard.__init__(self, config, plugins, storage)
        QDialog.__init__(self, None)
        self.setWindowTitle('Electrum-LTC  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        # Set for base base class
        self.language_for_seed = config.get('language')
        self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(_("Back"), self)
        self.back_button.setText(_('Back') if self.can_go_back() else _('Cancel'))
        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()
        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))
        outer_vbox = QVBoxLayout(self)
        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)
        scroll_widget = QWidget()
        scroll_widget.setLayout(inner_vbox)
        scroll = QScrollArea()
        scroll.setWidget(scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        icon_vbox = QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addWidget(scroll)
        hbox.setStretchFactor(scroll, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon('electrum-ltc.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def run_and_get_wallet(self, get_wallet_from_daemon):

        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-LTC wallet'))

        wallet_folder = os.path.dirname(self.storage.path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder)
            if path:
                self.name_e.setText(path)

        def on_filename(filename):
            path = os.path.join(wallet_folder, filename)
            wallet_from_memory = get_wallet_from_daemon(path)
            try:
                if wallet_from_memory:
                    self.storage = wallet_from_memory.storage
                else:
                    self.storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except BaseException:
                traceback.print_exc(file=sys.stderr)
                self.storage = None
                self.next_button.setEnabled(False)
            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 not wallet_from_memory:
                    if self.storage.is_encrypted_with_user_pw():
                        msg = _("This file is encrypted with a password.") + '\n' \
                              + _('Enter your password or choose another file.')
                        pw = True
                    elif self.storage.is_encrypted_with_hw_device():
                        msg = _("This file is encrypted using a hardware device.") + '\n' \
                              + _("Press 'Next' to choose device to decrypt.")
                        pw = False
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                        pw = False
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
                    pw = False
            else:
                msg = _('Cannot read file')
                pw = False
            self.msg_label.setText(msg)
            if pw:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        button.clicked.connect(on_choose)
        self.name_e.textChanged.connect(on_filename)
        n = os.path.basename(self.storage.path)
        self.name_e.setText(n)

        while True:
            if self.loop.exec_() != 2:  # 2 = next
                return
            if self.storage.file_exists() and not self.storage.is_encrypted():
                break
            if not self.storage.file_exists():
                break
            wallet_from_memory = get_wallet_from_daemon(self.storage.path)
            if wallet_from_memory:
                return wallet_from_memory
            if self.storage.file_exists() and self.storage.is_encrypted():
                if self.storage.is_encrypted_with_user_pw():
                    password = self.pw_e.text()
                    try:
                        self.storage.decrypt(password)
                        break
                    except InvalidPassword as e:
                        QMessageBox.information(None, _('Error'), str(e))
                        continue
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QMessageBox.information(None, _('Error'), str(e))
                        return
                elif self.storage.is_encrypted_with_hw_device():
                    try:
                        self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET)
                    except InvalidPassword as e:
                        QMessageBox.information(
                            None, _('Error'),
                            _('Failed to decrypt using this hardware device.') + '\n' +
                            _('If you use a passphrase, make sure it is correct.'))
                        self.reset_stack()
                        return self.run_and_get_wallet(get_wallet_from_daemon)
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QMessageBox.information(None, _('Error'), str(e))
                        return
                    if self.storage.is_past_initial_decryption():
                        break
                    else:
                        return
                else:
                    raise Exception('Unexpected encryption version')

        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _("The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n"
                    "Do you want to split your wallet into multiple files?").format(path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = _('Your accounts have been moved to') + ':\n' + file_list + '\n\n'+ _('Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        action = self.storage.get_action()
        if action and action not in ('new', 'upgrade_storage'):
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(_("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet

        self.wallet = Wallet(self.storage)
        return self.wallet

    def finished(self):
        """Called in hardware client wrapper, in order to close popups."""
        return

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            traceback.print_exception(*exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(QPixmap(icon_path(filename))
                            .scaledToWidth(60, mode=Qt.SmoothTransformation))
        return prior_filename

    def set_layout(self, layout, title=None, next_enabled=True):
        self.title.setText("<b>%s</b>"%title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)

    def exec_layout(self, layout, title=None, raise_on_cancel=True,
                        next_enabled=True):
        self.set_layout(layout, title, next_enabled)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack from None
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid, allow_multi=False):
        slayout = KeysLayout(parent=self, header_layout=message, is_valid=is_valid,
                             allow_multi=allow_multi)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_text()

    def seed_input(self, title, message, is_seed, options):
        slayout = SeedLayout(title=message, is_seed=is_seed, options=options, parent=self)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    @wizard_dialog
    def add_xpub_dialog(self, title, message, is_valid, run_next, allow_multi=False, show_wif_help=False):
        header_layout = QHBoxLayout()
        label = WWLabel(message)
        label.setMinimumWidth(400)
        header_layout.addWidget(label)
        if show_wif_help:
            header_layout.addWidget(InfoButton(WIF_HELP_TEXT), alignment=Qt.AlignRight)
        return self.text_input(title, header_layout, is_valid, allow_multi)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d"%index
        message = ' '.join([
            _('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.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, test):
        options = []
        if self.opt_ext:
            options.append('ext')
        if self.opt_bip39:
            options.append('bip39')
        title = _('Enter Seed')
        message = _('Please enter your seed phrase in order to restore your wallet.')
        return self.seed_input(title, message, test, options)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, test):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('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.')
        ])
        seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
        return seed

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        title =  _("Your wallet generation seed is:")
        slayout = SeedLayout(seed=seed_text, title=title, msg=True, options=['ext'])
        self.exec_layout(slayout)
        return slayout.is_ext

    def pw_layout(self, msg, kind, force_disable_encrypt_cb):
        playout = PasswordLayout(msg=msg, kind=kind, OK_button=self.next_button,
                                 force_disable_encrypt_cb=force_disable_encrypt_cb)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.new_password(), playout.encrypt_cb.isChecked()

    @wizard_dialog
    def request_password(self, run_next, force_disable_encrypt_cb=False):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW, force_disable_encrypt_cb)

    @wizard_dialog
    def request_storage_encryption(self, run_next):
        playout = PasswordLayoutForHW(MSG_HW_STORAGE_ENCRYPTION)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.encrypt_cb.isChecked()

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next):
        self.confirm(message, title)

    def confirm(self, message, title):
        label = WWLabel(message)
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        self.exec_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self):
        self.accept_signal.emit()

    def waiting_dialog(self, task, msg, on_finished=None):
        label = WWLabel(msg)
        vbox = QVBoxLayout()
        vbox.addSpacing(100)
        label.setMinimumWidth(300)
        label.setAlignment(Qt.AlignCenter)
        vbox.addWidget(label)
        self.set_layout(vbox, next_enabled=False)
        self.back_button.setEnabled(False)

        t = threading.Thread(target=task)
        t.start()
        while True:
            t.join(1.0/60)
            if t.is_alive():
                self.refresh_gui()
            else:
                break
        if on_finished:
            on_finished()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def choice_and_line_dialog(self, title: str, message1: str, choices: List[Tuple[str, str, str]],
                               message2: str, test_text: Callable[[str], int],
                               run_next, default_choice_idx: int=0) -> Tuple[str, str]:
        vbox = QVBoxLayout()

        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        c_default_text = [x[2] for x in choices]
        def on_choice_click(clayout):
            idx = clayout.selected_index()
            line.setText(c_default_text[idx])
        clayout = ChoicesLayout(message1, c_titles, on_choice_click,
                                checked_index=default_choice_idx)
        vbox.addLayout(clayout.layout())

        vbox.addSpacing(50)
        vbox.addWidget(WWLabel(message2))

        line = QLineEdit()
        def on_text_change(text):
            self.next_button.setEnabled(test_text(text))
        line.textEdited.connect(on_text_change)
        on_choice_click(clayout)  # set default text for "line"
        vbox.addWidget(line)

        self.exec_layout(vbox, title)
        choice = c_values[clayout.selected_index()]
        return str(line.text()), choice

    @wizard_dialog
    def line_dialog(self, run_next, title, message, default, test, warning='',
                    presets=(), warn_issue4566=False):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
            if warn_issue4566:
                text_whitespace_normalised = ' '.join(text.split())
                warn_issue4566_label.setVisible(text != text_whitespace_normalised)
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        warn_issue4566_label = WWLabel(MSG_PASSPHRASE_WARN_ISSUE4566)
        warn_issue4566_label.setVisible(False)
        vbox.addWidget(warn_issue4566_label)

        for preset in presets:
            button = QPushButton(preset[0])
            button.clicked.connect(lambda __, text=preset[1]: line.setText(text))
            button.setMinimumWidth(150)
            hbox = QHBoxLayout()
            hbox.addWidget(button, alignment=Qt.AlignCenter)
            vbox.addLayout(hbox)

        self.exec_layout(vbox, title, next_enabled=test(default))
        return line.text()

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedLayout(xpub, title=msg, icon=False, for_seed_words=False)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _("Electrum communicates with remote servers to get "
                  "information about your transactions and addresses. The "
                  "servers all fulfill the same purpose only differing in "
                  "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")]
        title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(message, choices)
        self.back_button.setText(_('Cancel'))
        self.exec_layout(clayout.layout(), title)
        r = clayout.selected_index()
        if r == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()
        else:
            network.auto_connect = True
            self.config.set_key('auto_connect', True, True)

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        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 {0} signatures').format(m))
            cw.set_m(m)
        def on_n(n):
            n_label.setText(_('From {0} cosigners').format(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)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:")))
        vbox.addLayout(grid)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)