Пример #1
0
 def on_filename(filename):
     path = os.path.join(wallet_folder, filename)
     try:
         self.storage = WalletStorage(path, manual_upgrades=True)
         self.next_button.setEnabled(True)
     except IOError:
         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 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
     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()
Пример #2
0
    def __init__(self, config, daemon, 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 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)

        set_verbosity(False)
        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

        if self.network:
            self.network.register_callback(self.update, ['updated'])

        self.tab_names = [
            _("History"),
            _("Send"),
            _("Receive"),
            _("Addresses"),
            _("Contacts"),
            _("Banner")
        ]
        self.num_tabs = len(self.tab_names)
Пример #3
0
 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 IOError:
         self.temp_storage = None
         self.next_button.setEnabled(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.")
             pw = False
         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.')
                 pw = 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.")
                 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()
Пример #4
0
    def start_new_window(self, path, uri, daemon):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it.'''
        for w in self.windows[daemon.currency]:
            if w.wallet.storage.path == path:
                w.bring_to_top()
                break
        else:
            try:

                wallet = daemon.load_wallet(path, None)
                if not wallet:
                    storage = WalletStorage(path,
                                            daemon.currency,
                                            manual_upgrades=True)
                    wizard = InstallWizard(self.config, self.app, self.plugins,
                                           daemon.currency, storage)
                    try:
                        wallet = wizard.run_and_get_wallet()
                    except UserCancelled:
                        pass
                    except GoBack as e:
                        print_error(
                            '[start_new_window] Exception caught (GoBack)', e)
                    finally:
                        wizard.terminate()
                    if not wallet:
                        return
                    wallet.start_threads(daemon.network)
                    daemon.add_wallet(wallet)
            except BaseException as e:
                traceback.print_exc(file=sys.stdout)
                if '2fa' in str(e):
                    d = QMessageBox(
                        QMessageBox.Warning, _('Error'),
                        '2FA wallets for Bitcoin Cash are currently unsupported by <a href="https://api.trustedcoin.com/#/">TrustedCoin</a>. Follow <a href="https://github.com/Electron-Cash/Electron-Cash/issues/41#issuecomment-357468208">this guide</a> in order to recover your funds.'
                    )
                    d.exec_()
                else:
                    d = QMessageBox(QMessageBox.Warning, _('Error'),
                                    'Cannot load wallet:\n' + str(e))
                    d.exec_()
                return
            w = self.create_window_for_wallet(wallet, daemon.currency)
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized
                         | QtCore.Qt.WindowActive)

        # this will activate the window
        w.activateWindow()

        return w
Пример #5
0
    def start_new_window(self, path, uri):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it.'''
        path = standardize_path(
            path)  # just make sure some plugin didn't give us a symlink
        for w in self.windows:
            if w.wallet.storage.path == path:
                w.bring_to_top()
                break
        else:
            try:
                wallet = self.daemon.load_wallet(path, None)
                if not wallet:
                    storage = WalletStorage(path, manual_upgrades=True)
                    wizard = InstallWizard(self.config, self.app, self.plugins,
                                           storage)
                    try:
                        wallet = wizard.run_and_get_wallet()
                    except UserCancelled:
                        pass
                    except GoBack as e:
                        self.print_error(
                            '[start_new_window] Exception caught (GoBack)', e)
                    finally:
                        wizard.terminate()
                        del wizard
                        gc.collect(
                        )  # wizard sticks around in memory sometimes, otherwise :/
                    if not wallet:
                        return
                    wallet.start_threads(self.daemon.network)
                    self.daemon.add_wallet(wallet)
            except BaseException as e:
                traceback.print_exc(file=sys.stdout)
                if '2fa' in str(e):
                    self.warning(
                        title=_('Error'),
                        message=
                        '2FA wallets for Bitcoin Cash are currently unsupported by <a href="https://api.trustedcoin.com/#/">TrustedCoin</a>. Follow <a href="https://github.com/Electron-Cash/Electron-Cash/issues/41#issuecomment-357468208">this guide</a> in order to recover your funds.'
                    )
                else:
                    self.warning(title=_('Error'),
                                 message='Cannot load wallet:\n' + str(e))
                return
            w = self.create_window_for_wallet(wallet)
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~Qt.WindowMinimized
                         | Qt.WindowActive)

        # this will activate the window
        w.activateWindow()
        return w
Пример #6
0
    def __init__(self, config, daemon, plugins):
        self.config = config
        self.network = daemon.network
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists:
            print(f"Wallet not found. try '{SCRIPT_NAME} create'")
            exit()
        if storage.is_encrypted():
            password = getpass.getpass('Password:', stream=None)
            storage.decrypt(password)

        self.done = 0
        self.last_balance = ""

        set_verbosity(False)

        self.str_recipient = ""
        self.str_description = ""
        self.str_amount = ""
        self.str_fee = ""

        self.wallet = Wallet(storage)
        self.wallet.start_threads(self.network)
        self.contacts = self.wallet.contacts

        self.network.register_callback(
            self.on_network,
            ["wallet_updated", "blockchain_updated", "banner"])
        self.commands = [
            _("[h] - displays this help text"),
            _("[i] - display transaction history"),
            _("[o] - enter payment order"),
            _("[p] - print stored payment order"),
            _("[s] - send stored payment order"),
            _("[r] - show own receipt addresses"),
            _("[c] - display contacts"),
            _("[b] - print server banner"),
            _("[q] - quit"),
        ]
        self.num_commands = len(self.commands)
Пример #7
0
 def load_wallet_by_name(self, path):
     if not path:
         return
     wallet = self.daemon.load_wallet(path, None)
     if wallet:
         if wallet != self.wallet:
             self.stop_wallet()
             self.load_wallet(wallet)
             self.on_resume()
     else:
         Logger.debug('Electrum: Wallet not found. Launching install wizard')
         storage = WalletStorage(path)
         wizard = Factory.InstallWizard(self.electrum_config, storage)
         wizard.bind(on_wizard_complete=self.on_wizard_complete)
         action = wizard.storage.get_action()
         wizard.run(action)
Пример #8
0
    def start_new_window(self, path, uri):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it.'''
        for w in self.windows:
            if w.wallet.storage.path == path:
                w.bring_to_top()
                break
        else:
            try:
                wallet = self.daemon.load_wallet(path, None)
            except BaseException as e:
                traceback.print_exc(file=sys.stdout)
                d = QMessageBox(QMessageBox.Warning, _('Error'),
                                'Cannot load wallet:\n' + str(e))
                d.exec_()
                return
            if not wallet:
                storage = WalletStorage(path, manual_upgrades=True)
                wizard = InstallWizard(self.config, self.app, self.plugins,
                                       storage)
                try:
                    wallet = wizard.run_and_get_wallet()
                except UserCancelled:
                    pass
                except GoBack as e:
                    print_error('[start_new_window] Exception caught (GoBack)',
                                e)
                wizard.terminate()
                if not wallet:
                    return
                wallet.start_threads(self.daemon.network)
                self.daemon.add_wallet(wallet)
            w = self.create_window_for_wallet(wallet)
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized
                         | QtCore.Qt.WindowActive)

        # this will activate the window
        w.activateWindow()
        return w
Пример #9
0
    def run(self):
        network = Network.get_instance()
        if not network:
            self.notify_offline()
            return

        for i, p in enumerate(self.DERIVATION_PATHS):
            if self.aborting:
                return
            k = keystore.from_seed(self.seed,
                                   '',
                                   derivation=p,
                                   seed_type=self.seed_type)
            p_safe = p.replace('/', '_').replace("'", 'h')
            storage_path = os.path.join(
                tempfile.gettempdir(), p_safe + '_' +
                random.getrandbits(32).to_bytes(4, 'big').hex()[:8] +
                "_not_saved_")
            tmp_storage = WalletStorage(storage_path, in_memory_only=True)
            tmp_storage.put('seed_type', self.seed_type)
            tmp_storage.put('keystore', k.dump())
            wallet = Standard_Wallet(tmp_storage)
            try:
                wallet.start_threads(network)
                wallet.synchronize()
                wallet.print_error("Scanning", p)
                synched = False
                for ctr in range(25):
                    try:
                        wallet.wait_until_synchronized(timeout=1.0)
                        synched = True
                    except TimeoutException:
                        wallet.print_error(f'timeout try {ctr+1}/25')
                    if self.aborting:
                        return
                if not synched:
                    wallet.print_error("Timeout on", p)
                    self.notify_timedout(i)
                    continue
                while network.is_connecting():
                    time.sleep(0.1)
                    if self.aborting:
                        return
                num_tx = len(wallet.get_history())
                self.update_table_cb(i, str(num_tx))
            finally:
                wallet.clear_history()
                wallet.stop_threads()
Пример #10
0
 def start_new_window(self, path, uri):
     '''Raises the window for the wallet if it is open.  Otherwise
     opens the wallet and creates a new window for it.'''
     for w in self.windows:
         if w.wallet.storage.path == path:
             w.bring_to_top()
             break
     else:
         wallet = self.daemon.load_wallet(path, None)
         if not wallet:
             storage = WalletStorage(path)
             wizard = InstallWizard(self.config, self.app, self.plugins, storage)
             wallet = wizard.run_and_get_wallet()
             wizard.terminate()
             if not wallet:
                 return
             wallet.start_threads(self.daemon.network)
             self.daemon.add_wallet(wallet)
         w = self.create_window_for_wallet(wallet)
     if uri:
         w.pay_to_URI(uri)
     return w
Пример #11
0
    def select_storage(
        self, path, get_wallet_from_daemon
    ) -> Tuple[str, Optional[WalletStorage]]:
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(
            QtWidgets.QLabel(
                _('Create a new wallet or load an existing wallet from file.'))
        )
        vbox.addSpacing(20)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(QtWidgets.QLabel(_('Wallet name') + ':'))
        self.name_e = QtWidgets.QLineEdit()
        self.name_e.setToolTip(
            _("Enter a wallet name (new or existing), or the path to a wallet file.")
        )
        hbox.addWidget(self.name_e)
        button = QtWidgets.QPushButton(_('Load...'))
        button.setToolTip(_("Open a file selection dialog to load a wallet file."))
        hbox.addWidget(button)
        vbox.addLayout(hbox)
        vbox.addSpacing(20)

        self.msg_label = QtWidgets.QLabel('')
        vbox.addWidget(self.msg_label)
        hbox2 = QtWidgets.QHBoxLayout()
        self.pw_e = PasswordLineEdit('', self)
        self.pw_e.setFixedWidth(17 * char_width_in_lineedit())
        self.pw_label = QtWidgets.QLabel(_('Password') + ':')
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self.set_layout(vbox, title=_(f'{PROJECT_NAME} wallet'))

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

        def on_choose():
            path, __ = QtWidgets.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 IOError:
                self.temp_storage = None
                self.next_button.setEnabled(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.")
                    pw = False
                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.')
                        pw = 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.")
                        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.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:
                        QtWidgets.QMessageBox.information(None, _('Error'), str(e))
                        continue
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QtWidgets.QMessageBox.information(None, _('Error'), 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:
                        QtWidgets.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.select_storage(path, get_wallet_from_daemon)
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QtWidgets.QMessageBox.information(None, _('Error'), 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)
Пример #12
0
class InstallWizard(QtWidgets.QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    def __init__(self, config, app, plugins):
        BaseWizard.__init__(self, config)
        QtWidgets.QDialog.__init__(self, None)
        self.setWindowTitle(f'{PROJECT_NAME}  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        # Set for base base class
        self.plugins = plugins
        self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QtWidgets.QLabel()
        self.main_widget = QtWidgets.QWidget()
        self.back_button = QtWidgets.QPushButton(_("Back"), self)
        self.back_button.setText(_('Back') if self.can_go_back() else _('Cancel'))
        self.next_button = QtWidgets.QPushButton(_("Next"), self)
        self.next_button.setDefault(True)
        self.logo = QtWidgets.QLabel()
        self.please_wait = QtWidgets.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 = QtWidgets.QVBoxLayout(self)
        inner_vbox = QtWidgets.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 = QtWidgets.QWidget()
        scroll_widget.setLayout(inner_vbox)
        scroll = QtWidgets.QScrollArea()
        scroll.setWidget(scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        icon_vbox = QtWidgets.QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QtWidgets.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(':icons/electrumABC.svg')
        self.show()
        self.raise_()

        # Track object lifecycle
        finalization_print_error(self)

    def select_storage(
        self, path, get_wallet_from_daemon
    ) -> Tuple[str, Optional[WalletStorage]]:
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(
            QtWidgets.QLabel(
                _('Create a new wallet or load an existing wallet from file.'))
        )
        vbox.addSpacing(20)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(QtWidgets.QLabel(_('Wallet name') + ':'))
        self.name_e = QtWidgets.QLineEdit()
        self.name_e.setToolTip(
            _("Enter a wallet name (new or existing), or the path to a wallet file.")
        )
        hbox.addWidget(self.name_e)
        button = QtWidgets.QPushButton(_('Load...'))
        button.setToolTip(_("Open a file selection dialog to load a wallet file."))
        hbox.addWidget(button)
        vbox.addLayout(hbox)
        vbox.addSpacing(20)

        self.msg_label = QtWidgets.QLabel('')
        vbox.addWidget(self.msg_label)
        hbox2 = QtWidgets.QHBoxLayout()
        self.pw_e = PasswordLineEdit('', self)
        self.pw_e.setFixedWidth(17 * char_width_in_lineedit())
        self.pw_label = QtWidgets.QLabel(_('Password') + ':')
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self.set_layout(vbox, title=_(f'{PROJECT_NAME} wallet'))

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

        def on_choose():
            path, __ = QtWidgets.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 IOError:
                self.temp_storage = None
                self.next_button.setEnabled(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.")
                    pw = False
                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.')
                        pw = 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.")
                        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.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:
                        QtWidgets.QMessageBox.information(None, _('Error'), str(e))
                        continue
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QtWidgets.QMessageBox.information(None, _('Error'), 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:
                        QtWidgets.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.select_storage(path, get_wallet_from_daemon)
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QtWidgets.QMessageBox.information(None, _('Error'), 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

            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 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(QIcon(filename).pixmap(60))
        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:
            QtWidgets.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, title=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, editable=True)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    def bip38_prompt_for_pw(self, bip38_keys):
        ''' Reimplemented from basewizard superclass. Expected to return the pw
        dict or None. '''
        d = Bip38Importer(bip38_keys, parent=self.top_level_window())
        res = d.exec_()
        d.setParent(None)  # python GC quicker if this happens
        return d.decoded_keys  # dict will be empty if user cancelled

    @wizard_dialog
    def add_xpub_dialog(self, title, message, is_valid, run_next, allow_multi=False):
        return self.text_input(title, message, 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, editable=True):
        title =  _("Your wallet generation seed is:")
        slayout = SeedLayout(seed=seed_text, title=title, msg=True, options=['ext'], editable=False)
        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.  Note that this dialog screen
        cannot go back, and instead the user can only cancel."""
        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()

    @staticmethod
    def _add_extra_button_to_layout(extra_button, layout):
        if (not isinstance(extra_button, (list, tuple))
                or not len(extra_button) == 2):
            return
        but_title, but_action = extra_button
        hbox = QtWidgets.QHBoxLayout()
        hbox.setContentsMargins(12,24,12,12)
        but = QtWidgets.QPushButton(but_title)
        hbox.addStretch(1)
        hbox.addWidget(but)
        layout.addLayout(hbox)
        but.clicked.connect(but_action)

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

    def confirm(self, message, title, extra_button=None):
        label = WWLabel(message)

        textInteractionFlags = (Qt.LinksAccessibleByMouse
                                | Qt.TextSelectableByMouse
                                | Qt.TextSelectableByKeyboard
                                | Qt.LinksAccessibleByKeyboard)
        label.setTextInteractionFlags(textInteractionFlags)
        label.setOpenExternalLinks(True)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(label)
        if extra_button:
            self._add_extra_button_to_layout(extra_button, vbox)
        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 run_task_without_blocking_gui(self, task, *, msg=None):
        assert self.gui_thread == threading.current_thread(), 'must be called from GUI thread'
        if msg is None:
            msg = _("Please wait...")

        exc: Optional[Exception] = None
        res = None
        def task_wrapper():
            nonlocal exc
            nonlocal res
            try:
                task()
            except Exception as e:
                exc = e
        self.waiting_dialog(task_wrapper, msg=msg)
        if exc is None:
            return res
        else:
            raise exc

    def waiting_dialog(self, task, msg, on_finished=None):
        label = WWLabel(msg)
        vbox = QtWidgets.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, extra_button=None):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(clayout.layout())
        if extra_button:
            self._add_extra_button_to_layout(extra_button, vbox)
        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 = QtWidgets.QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def line_dialog(self, run_next, title, message, default, test, warning=''):
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QtWidgets.QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))
        self.exec_layout(vbox, title, next_enabled=test(default))
        return ' '.join(line.text().split())

    @wizard_dialog
    def derivation_path_dialog(self, run_next, title, message, default, test, warning='', seed='', scannable=False):
        def on_derivation_scan(derivation_line, seed):
            derivation_scan_dialog = DerivationDialog(self, seed, DerivationPathScanner.DERIVATION_PATHS)
            destroyed_print_error(derivation_scan_dialog)
            selected_path = derivation_scan_dialog.get_selected_path()
            if selected_path:
                derivation_line.setText(selected_path)
            derivation_scan_dialog.deleteLater()

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QtWidgets.QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        if scannable:
            hbox = QtWidgets.QHBoxLayout()
            hbox.setContentsMargins(12,24,12,12)
            but = QtWidgets.QPushButton(_("Scan Derivation Paths..."))
            hbox.addStretch(1)
            hbox.addWidget(but)
            vbox.addLayout(hbox)
            but.clicked.connect(lambda: on_derivation_scan(line, seed))

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

    @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 = QtWidgets.QVBoxLayout()
        layout = SeedLayout(xpub, title=msg, icon=False)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _(
            f"{PROJECT_NAME} communicates with remote servers to get "
            "information about your transactions and addresses. The "
            "servers all fulfil the same purpose only differing in "
            f"hardware. In most cases you simply want to let {PROJECT_NAME} "
            "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()
        network.auto_connect = (r == 0)
        self.config.set_key('auto_connect', network.auto_connect, True)
        if r == 1:
            nlayout = NetworkChoiceLayout(self, network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QtWidgets.QSlider(Qt.Horizontal, self)
        n_edit = QtWidgets.QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(1)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QtWidgets.QLabel()
        m_label = QtWidgets.QLabel()
        grid = QtWidgets.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)
        vbox = QtWidgets.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)

    linux_hw_wallet_support_dialog = None

    def on_hw_wallet_support(self):
        ''' Overrides base wizard's noop impl. '''
        if sys.platform.startswith("linux"):
            if self.linux_hw_wallet_support_dialog:
                self.linux_hw_wallet_support_dialog.raise_()
                return
            # NB: this should only be imported from Linux
            from . import udev_installer
            self.linux_hw_wallet_support_dialog = udev_installer.InstallHardwareWalletSupportDialog(self.top_level_window(), self.plugins)
            self.linux_hw_wallet_support_dialog.exec_()
            self.linux_hw_wallet_support_dialog.setParent(None)
            self.linux_hw_wallet_support_dialog = None
        else:
            self.show_error("Linux only facility. FIXME!")
Пример #13
0
    def start_new_window(self, path, uri):
        '''Raises the window for the wallet if it is open. Otherwise
        opens the wallet and creates a new window for it.

        `path=None` is a special usage which will raise the last activated
        window or open the 'last wallet' if no windows are open.'''

        if not path:
            if not self.windows:
                # This branch is taken if nothing is currently open but
                # path == None, in which case set path=last wallet
                self.config.open_last_wallet()
                path = self.config.get_wallet_path()
            elif self._last_active_window:
                # This branch is taken if we have windows open and we have
                # _last_active_window defined, in which case we specify
                # that this window should be activated by setting path
                # so that the for loop below will trigger on this window.
                w = self._last_active_window()  # weak ref -> strong ref
                if w and w in self.windows:  # check ref still alive
                    # this will cause the last active window to be used in the
                    # for loop below
                    path = w.wallet.storage.path

        # NB: path may still be None here if it came in as None from args and
        # if the above logic couldn't select a window to use -- in which case
        # we'll end up picking self.windows[0]

        path = path and standardize_path(
            path)  # just make sure some plugin didn't give us a symlink
        for w in self.windows:
            if not path or w.wallet.storage.path == path:
                path = w.wallet.storage.path  # remember path in case it was None
                w.bring_to_top()
                break
        else:
            try:

                try:
                    wallet = self.daemon.load_wallet(path, None)
                except BaseException as e:
                    self.print_error(repr(e))
                    if self.windows:
                        # *Not* starting up. Propagate exception out to present
                        # error message box to user.
                        raise e
                    # We're just starting up, so we are tolerant of bad wallets
                    # and just want to proceed to the InstallWizard so the user
                    # can either specify a different wallet or create a new one.
                    # (See issue #1189 where before they would get stuck)
                    path = self.get_new_wallet_path(
                    )  # give up on this unknown wallet and try a new name.. note if things get really bad this will raise FileNotFoundError and the app aborts here.
                    wallet = None  # fall thru to wizard
                if not wallet:
                    storage = WalletStorage(path, manual_upgrades=True)
                    wizard = InstallWizard(self.config, self.app, self.plugins,
                                           storage, 'New/Restore Wallet')
                    try:
                        wallet = wizard.run_and_get_wallet()
                    except UserCancelled:
                        pass
                    except GoBack as e:
                        self.print_error(
                            '[start_new_window] Exception caught (GoBack)', e)
                    finally:
                        wizard.terminate()
                        del wizard
                        gc.collect(
                        )  # wizard sticks around in memory sometimes, otherwise :/
                    if not wallet:
                        return
                    wallet.start_threads(self.daemon.network)
                    self.daemon.add_wallet(wallet)
            except BaseException as e:
                traceback.print_exc(file=sys.stdout)
                if '2fa' in str(e):
                    self.warning(
                        title=_('Error'),
                        message=
                        '2FA wallets for Bitcoin Cash are currently unsupported by <a href="https://api.trustedcoin.com/#/">TrustedCoin</a>. Follow <a href="https://github.com/Electron-Cash/Electron-Cash/issues/41#issuecomment-357468208">this guide</a> in order to recover your funds.'
                    )
                else:
                    self.warning(title=_('Error'),
                                 message='Cannot load wallet:\n' + str(e),
                                 icon=QMessageBox.Critical)
                return
            w = self.create_window_for_wallet(wallet)
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~Qt.WindowMinimized
                         | Qt.WindowActive)

        # this will activate the window
        w.activateWindow()
        return w
Пример #14
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
    def __init__(self, config, app, plugins, storage):
        BaseWizard.__init__(self, config, storage)
        QDialog.__init__(self, None)
        self.setWindowTitle('Electron Cash  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        # Set for base base class
        self.plugins = plugins
        self.language_for_seed = config.get('language')
        self.setMinimumSize(600, 400)
        self.connect(self, QtCore.SIGNAL('accept'), 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 = 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)
        hbox.addSpacing(5)
        hbox.addLayout(inner_vbox)
        hbox.setStretchFactor(inner_vbox, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon(':icons/electrum.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def run_and_get_wallet(self):

        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=_('Electron Cash wallet'))

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

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

        def on_filename(filename):
            filename = unicode(filename)
            path = os.path.join(wallet_folder, filename.encode('utf8'))
            try:
                self.storage = WalletStorage(path)
            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
            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.decode('utf8'))

        while True:
            if self.storage.file_exists() and not self.storage.is_encrypted():
                break
            if self.loop.exec_() != 2:  # 2 = next
                return
            if not self.storage.file_exists():
                break
            if self.storage.file_exists() and self.storage.is_encrypted():
                password = unicode(self.pw_e.text())
                try:
                    self.storage.decrypt(password)
                    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

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

        if self.storage.requires_upgrade():
            self.hide()
            msg = _(
                "The format of your wallet '%s' must be upgraded for Electron Cash. 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':
            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()
        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(filename).scaledToWidth(60))
        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
        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):
        slayout = KeysLayout(parent=self, title=message, is_valid=is_valid)
        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, slayout.is_bip39_145

    @wizard_dialog
    def add_xpub_dialog(self, title, message, is_valid, run_next):
        return self.text_input(title, message, is_valid)

    @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')
        if self.opt_bip39_145:
            options.append('bip39_145')
        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, is_bip39_145 = 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):
        playout = PasswordLayout(None, msg, kind, self.next_button)
        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):
        """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)

    def show_restore(self, wallet, network):
        # 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...
        if network:

            def task():
                wallet.wait_until_synchronized()
                if wallet.is_found():
                    msg = _("Recovery successful")
                else:
                    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)
            t.daemon = True
            t.start()
        else:
            msg = _("This wallet was restored offline. It may "
                    "contain more addresses than displayed.")
            self.show_message(msg)

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

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

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

    def terminate(self):
        self.emit(QtCore.SIGNAL('accept'))

    def waiting_dialog(self, task, msg):
        self.please_wait.setText(MSG_GENERATING_WAIT)
        self.refresh_gui()
        t = threading.Thread(target=task)
        t.start()
        t.join()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = map(lambda x: x[0], choices)
        c_titles = map(lambda x: x[1], 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 line_dialog(self, run_next, title, message, default, test, warning=''):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)

        def f(text):
            self.next_button.setEnabled(test(unicode(text)))

        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))
        self.exec_layout(vbox, title, next_enabled=test(default))
        return ' '.join(unicode(line.text()).split())

    @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)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _(
            "Electron Cash communicates with remote servers to get "
            "information about your transactions and addresses. The "
            "servers all fulfil the same purpose only differing in "
            "hardware. In most cases you simply want to let Electron Cash "
            "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()
        network.auto_connect = (r == 0)
        self.config.set_key('auto_connect', network.auto_connect, True)
        if r == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()

    @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 %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)
        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)
Пример #15
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()
    synchronized_signal = pyqtSignal(str)

    def __init__(self, config, app, plugins, storage, partial_title='Install Wizard'):
        BaseWizard.__init__(self, config, storage)
        QDialog.__init__(self, None)
        self.setWindowTitle('ECash SLPDB -  ' + _(partial_title))
        self.app = app
        self.config = config
        # Set for base base class
        self.plugins = plugins
        self.setMinimumSize(600, 450)
        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(':icons/electron-cash.svg')
        self.show()
        self.raise_()

        # Track object lifecycle
        finalization_print_error(self)

    def run_and_get_wallet(self):

        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)

        logo = QLabel()
        logo.setPixmap(QPixmap(":icons/slp_logo_hollow.png").scaledToWidth(52))
        logo.setMaximumWidth(52)
        vbox.addWidget(QLabel(_("<hr><b>NOTE: This wallet is SLP token aware.</b>")))
        vbox.addWidget(logo)
        vbox.addWidget(QLabel(_("New wallets SLP use m/44'/245'/0' as the address derivation path.") + '\n' \
                                + _("Funds will not be accessible with non-SLP versions of Electron Cash.")))

        vbox.addWidget(QLabel(_("To avoid losing SLP tokens, you should avoid opening a wallet on") + '\n' \
                                + _("wallet software not aware of SLP tokens.")))

        vbox.addWidget(QLabel(_("For more information visit: <a href=\"https://SimpleLedger.cash\">https://SimpleLedger.cash</a>")))

        self.set_layout(vbox, title=_('ECash SLPDB 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)
            try:
                self.storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except IOError:
                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 SLP 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
            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:
            password = None
            if self.storage.file_exists() and not self.storage.is_encrypted():
                break
            if self.loop.exec_() != 2:  # 2 = next
                return
            if not self.storage.file_exists():
                break
            if self.storage.file_exists() and self.storage.is_encrypted():
                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

        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

        if self.storage.requires_upgrade():
            self.hide()
            msg = _("The format of your wallet '%s' must be upgraded for Electron Cash. This change will not be backward compatible"%path)
            if not self.question(msg):
                return
            self.storage.upgrade()
            self.wallet = Wallet(self.storage)
            return self.wallet, password

        action = self.storage.get_action()
        if action and action != 'new':
            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, password

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

    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(QIcon(filename).pixmap(60))
        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
        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, title=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, can_skip=None):
        slayout = SeedLayout(title=message, is_seed=is_seed, options=options, parent=self, editable=True, can_skip=can_skip)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext, slayout.was_skipped

    def bip38_prompt_for_pw(self, bip38_keys):
        ''' Reimplemented from basewizard superclass. Expected to return the pw
        dict or None. '''
        d = Bip38Importer(bip38_keys, parent=self.top_level_window())
        res = d.exec_()
        d.setParent(None)  # python GC quicker if this happens
        return d.decoded_keys  # dict will be empty if user cancelled

    @wizard_dialog
    def add_xpub_dialog(self, title, message, is_valid, run_next, allow_multi=False):
        return self.text_input(title, message, 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)[:3]

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, test, can_skip=True):
        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, was_skipped = self.seed_input(title, message, test, options=None, can_skip=can_skip)
        return seed, was_skipped

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

    def pw_layout(self, msg, kind):
        playout = PasswordLayout(None, msg, kind, self.next_button)
        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):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password.  Note that this dialog screen
        cannot go back, and instead the user can only cancel."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW)

    @staticmethod
    def _add_extra_button_to_layout(extra_button, layout):
        if (not isinstance(extra_button, (list, tuple))
                or not len(extra_button) == 2):
            return
        but_title, but_action = extra_button
        hbox = QHBoxLayout()
        hbox.setContentsMargins(12,24,12,12)
        but = QPushButton(but_title)
        hbox.addStretch(1)
        hbox.addWidget(but)
        layout.addLayout(hbox)
        but.clicked.connect(but_action)

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

    def confirm(self, message, title, extra_button=None):
        label = WWLabel(message)
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        if extra_button:
            self._add_extra_button_to_layout(extra_button, vbox)
        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):
        self.please_wait.setText(MSG_GENERATING_WAIT)
        self.refresh_gui()
        t = threading.Thread(target = task)
        t.start()
        t.join()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next, extra_button=None, *, disabled_indices=set(), disabled_tooltip=''):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        non_disabled = [i for i,v in enumerate(c_titles) if i not in disabled_indices]
        if non_disabled:
            # if there are any non-disabled items, make the first one pre-selected
            checked_index = non_disabled[0]
        else:
            # otherwise ensure nothing is pre-selected
            checked_index = -1
        clayout = ChoicesLayout(message, c_titles, checked_index = checked_index,
                                disabled_indices = disabled_indices,
                                disabled_tooltip = disabled_tooltip)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        if extra_button:
            self._add_extra_button_to_layout(extra_button, vbox)
        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 line_dialog(self, run_next, title, message, default, test, warning=''):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))
        self.exec_layout(vbox, title, next_enabled=test(default))
        return ' '.join(line.text().split())

    @wizard_dialog
    def derivation_path_dialog(self, run_next, title, message, default, test, warning='', seed='', scannable=False):
        def on_derivation_scan(derivation_line, seed):
            derivation_scan_dialog = DerivationDialog(self, seed, DerivationPathScanner.DERIVATION_PATHS)
            destroyed_print_error(derivation_scan_dialog)
            selected_path = derivation_scan_dialog.get_selected_path()
            if selected_path:
                derivation_line.setText(selected_path)
            derivation_scan_dialog.deleteLater()

        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        if scannable:
            hbox = QHBoxLayout()
            hbox.setContentsMargins(12,24,12,12)
            but = QPushButton(_("Scan Derivation Paths..."))
            hbox.addStretch(1)
            hbox.addWidget(but)
            vbox.addLayout(hbox)
            but.clicked.connect(lambda: on_derivation_scan(line, seed))

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

    @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)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _("Electron Cash communicates with remote servers to get "
                    "information about your transactions and addresses. The "
                    "servers all fulfil the same purpose only differing in "
                    "hardware. In most cases you simply want to let Electron Cash "
                    "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()
        network.auto_connect = (r == 0)
        self.config.set_key('auto_connect', network.auto_connect, True)
        if r == 1:
            nlayout = NetworkChoiceLayout(self, network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()

    @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(1)
        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 %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)
        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)

    linux_hw_wallet_support_dialog = None

    def on_hw_wallet_support(self):
        ''' Overrides base wizard's noop impl. '''
        if sys.platform.startswith("linux"):
            if self.linux_hw_wallet_support_dialog:
                self.linux_hw_wallet_support_dialog.raise_()
                return
            # NB: this should only be imported from Linux
            from . import udev_installer
            self.linux_hw_wallet_support_dialog = udev_installer.InstallHardwareWalletSupportDialog(self.top_level_window(), self.plugins)
            self.linux_hw_wallet_support_dialog.exec_()
            self.linux_hw_wallet_support_dialog.setParent(None)
            self.linux_hw_wallet_support_dialog = None
        else:
            self.show_error("Linux only facility. FIXME!")
Пример #16
0
    def start_new_window(self, path, uri):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it.'''
        path = standardize_path(path)  # just make sure some plugin didn't give us a symlink
        for w in self.windows:
            if w.wallet.storage.path == path:
                w.bring_to_top()
                break
        else:
            try:

                if not self.windows:
                    self.warn_if_no_secp()

                try:
                    wallet = self.daemon.load_wallet(path, None)
                except BaseException as e:
                    self.print_error(repr(e))
                    if self.windows:
                        # *Not* starting up. Propagate exception out to present
                        # error message box to user.
                        raise e
                    # We're just starting up, so we are tolerant of bad wallets
                    # and just want to proceed to the InstallWizard so the user
                    # can either specify a different wallet or create a new one.
                    # (See issue #1189 where before they would get stuck)
                    path = self.get_new_wallet_path()  # give up on this unknown wallet and try a new name.. note if things get really bad this will raise FileNotFoundError and the app aborts here.
                    wallet = None  # fall thru to wizard
                if not wallet:
                    storage = WalletStorage(path, manual_upgrades=True)
                    wizard = InstallWizard(self.config, self.app, self.plugins, storage)
                    try:
                        wallet, password = wizard.run_and_get_wallet() or (None, None)
                    except UserCancelled:
                        pass
                    except GoBack as e:
                        self.print_error('[start_new_window] Exception caught (GoBack)', e)
                    finally:
                        wizard.terminate()
                        del wizard
                        gc.collect() # wizard sticks around in memory sometimes, otherwise :/
                    if not wallet:
                        return
                    wallet.start_threads(self.daemon.network)
                    self.daemon.add_wallet(wallet)
                    self._cache_password(wallet, password)
            except BaseException as e:
                traceback.print_exc(file=sys.stdout)
                if '2fa' in str(e):
                    self.warning(title=_('Error'), message = '2FA wallets for Bitcoin Cash are currently unsupported by <a href="https://api.trustedcoin.com/#/">TrustedCoin</a>. Follow <a href="https://github.com/Electron-Cash/Electron-Cash/issues/41#issuecomment-357468208">this guide</a> in order to recover your funds.')
                else:
                    self.warning(title=_('Error'), message = 'Cannot load wallet:\n' + str(e), icon=QMessageBox.Critical)
                return
            w = self.create_window_for_wallet(wallet)
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)

        # this will activate the window
        w.activateWindow()
        return w