Example #1
0
 def __init__(self,
              parent: QWidget,
              message: str,
              task,
              on_success=None,
              on_error=None):
     assert parent
     if isinstance(parent, MessageBoxMixin):
         parent = parent.top_level_window()
     WindowModalDialog.__init__(self, parent, _("Please wait"))
     self.message_label = QLabel(message)
     vbox = QVBoxLayout(self)
     vbox.addWidget(self.message_label)
     self.accepted.connect(self.on_accepted)
     self.show()
     self.thread = TaskThread(self)
     self.thread.finished.connect(self.deleteLater)  # see #3956
     self.thread.add(task, on_success, self.accept, on_error)
Example #2
0
 def add_copy_menu(self, menu, idx):
     cc = menu.addMenu(_("Copy"))
     for column in HistoryColumns:
         if column == HistoryColumns.TX_GROUP:
             continue
         if self.isColumnHidden(column):
             continue
         column_title = self.hm.headerData(column, Qt.Horizontal,
                                           Qt.DisplayRole)
         idx2 = idx.sibling(idx.row(), column)
         tx_item = self.hm.data(idx2, Qt.DisplayRole)
         if not tx_item:
             continue
         column_data = (tx_item.value() or '').strip()
         cc.addAction(column_title,
                      lambda text=column_data, title=column_title: self.
                      place_text_on_clipboard(text, title=title))
     return cc
 def on_send(self):
     password = self.pw.text() or None
     if self.password_required:
         if password is None:
             self.main_window.show_error(_("Password required"),
                                         parent=self)
             return
         try:
             psman = self.wallet.psman
             if psman.is_hw_ks and psman.is_ps_ks_encrypted():
                 psman.ps_keystore.check_password(password)
             else:
                 self.wallet.check_password(password)
         except Exception as e:
             self.main_window.show_error(str(e), parent=self)
             return
     self.is_send = True
     self.accept()
Example #4
0
    def setup_device(self, device_info, wizard, purpose):
        device_id = device_info.device.id_
        client = self.scan_and_create_client_for_device(device_id=device_id,
                                                        wizard=wizard)

        if not client.is_uptodate():
            msg = (_('Outdated {} firmware for device labelled {}. Please '
                     'download the updated firmware from {}').format(
                         self.device, client.label(), self.firmware_URL))
            raise OutdatedHwFirmwareException(msg)

        if not device_info.initialized:
            self.initialize_device(device_id, wizard, client.handler)
        is_creating_wallet = purpose == HWD_SETUP_NEW_WALLET
        wizard.run_task_without_blocking_gui(task=lambda: client.get_xpub(
            'm', 'standard', creating=is_creating_wallet))
        client.used()
        return client
Example #5
0
 def __init__(self, config: 'SimpleConfig', app: QApplication,
              plugins: 'Plugins', *, gui_object: 'ElectrumGui'):
     QDialog.__init__(self, None)
     BaseWizard.__init__(self, config, plugins)
     self.setWindowTitle('Dash Electrum  -  ' + _('Install Wizard'))
     self.app = app
     self.config = config
     self.gui_thread = gui_object.gui_thread
     self.setMinimumSize(600, 400)
     self.accept_signal.connect(self.accept)
     self.title = QLabel()
     self.main_widget = QWidget()
     self.back_button = QPushButton(_("Back"), self)
     self.back_button.setText(
         _('Back') if self.can_go_back() else _('Cancel'))
     self.next_button = QPushButton(_("Next"), self)
     self.next_button.setDefault(True)
     self.logo = QLabel()
     self.please_wait = QLabel(_("Please wait..."))
     self.please_wait.setAlignment(Qt.AlignCenter)
     self.icon_filename = None
     self.loop = QEventLoop()
     self.rejected.connect(lambda: self.loop.exit(0))
     self.back_button.clicked.connect(lambda: self.loop.exit(1))
     self.next_button.clicked.connect(lambda: self.loop.exit(2))
     outer_vbox = QVBoxLayout(self)
     inner_vbox = QVBoxLayout()
     inner_vbox.addWidget(self.title)
     inner_vbox.addWidget(self.main_widget)
     inner_vbox.addStretch(1)
     inner_vbox.addWidget(self.please_wait)
     inner_vbox.addStretch(1)
     scroll_widget = QWidget()
     scroll_widget.setLayout(inner_vbox)
     scroll = QScrollArea()
     scroll.setWidget(scroll_widget)
     scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
     scroll.setWidgetResizable(True)
     icon_vbox = QVBoxLayout()
     icon_vbox.addWidget(self.logo)
     icon_vbox.addStretch(1)
     hbox = QHBoxLayout()
     hbox.addLayout(icon_vbox)
     hbox.addSpacing(5)
     hbox.addWidget(scroll)
     hbox.setStretchFactor(scroll, 1)
     outer_vbox.addLayout(hbox)
     outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
     self.set_icon('electrum-dash.png')
     self.show()
     self.raise_()
     self.refresh_gui()  # Need for QT on MacOSX.  Lame.
Example #6
0
    def calibration_dialog(self, window):
        d = WindowModalDialog(window,
                              _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(
            QLabel(''.join([
                "<br/>",
                _("If you have an old printer, or want optimal precision"),
                "<br/>",
                _("print the calibration pdf and follow the instructions "),
                "<br/>",
                "<br/>",
            ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Example #7
0
    def receive_menu(self, menu, addrs, wallet: Abstract_Wallet):
        if type(wallet) is not Standard_Wallet:
            return

        keystore = wallet.get_keystore()
        if type(keystore) is not self.keystore_class:
            return

        if not self.is_mobile_paired():
            return

        if len(addrs) == 1:
            addr = addrs[0]
            if wallet.get_txin_type(addr) != 'p2pkh':
                return
            def show_address():
                keystore.thread.add(partial(self.show_address, wallet, addr, keystore))

            menu.addAction(_("Show on {}").format(self.device), show_address)
Example #8
0
 def create_menu(self, position):
     wallet = self.parent.wallet
     items = self.selected_in_column(0)
     if len(items) > 1:
         keys = [item.data(ROLE_REQUEST_ID) for item in items]
         invoices = [wallet.invoices.get(key) for key in keys]
         can_batch_pay = all([
             i.type == PR_TYPE_ONCHAIN
             and wallet.get_invoice_status(i) == PR_UNPAID for i in invoices
         ])
         menu = QMenu(self)
         if can_batch_pay:
             menu.addAction(
                 _("Batch pay invoices") + "...",
                 lambda: self.parent.pay_multiple_invoices(invoices))
         menu.addAction(_("Delete invoices"),
                        lambda: self.parent.delete_invoices(keys))
         menu.exec_(self.viewport().mapToGlobal(position))
         return
     idx = self.indexAt(position)
     item = self.item_from_index(idx)
     item_col0 = self.item_from_index(
         idx.sibling(idx.row(), self.Columns.DATE))
     if not item or not item_col0:
         return
     key = item_col0.data(ROLE_REQUEST_ID)
     invoice = self.parent.wallet.get_invoice(key)
     menu = QMenu(self)
     self.add_copy_menu(menu, idx)
     if len(invoice.outputs) == 1:
         menu.addAction(
             _("Copy Address"),
             lambda: self.parent.do_copy(invoice.get_address(),
                                         title='Dash Address'))
     menu.addAction(_("Details"),
                    lambda: self.parent.show_onchain_invoice(invoice))
     status = wallet.get_invoice_status(invoice)
     if status == PR_UNPAID:
         menu.addAction(
             _("Pay") + "...", lambda: self.parent.do_pay_invoice(invoice))
     if status == PR_FAILED:
         menu.addAction(_("Retry"),
                        lambda: self.parent.do_pay_invoice(invoice))
     menu.addAction(_("Delete"), lambda: self.parent.delete_invoices([key]))
     menu.exec_(self.viewport().mapToGlobal(position))
Example #9
0
    def on_task_thread_error(self: Union['QtPluginBase', HW_PluginBase],
                             window: ElectrumWindow,
                             keystore: 'Hardware_KeyStore', exc_info):
        e = exc_info[1]
        if isinstance(e, OutdatedHwFirmwareException):
            if window.question(e.text_ignore_old_fw_and_continue(),
                               title=_("Outdated device firmware")):
                self.set_ignore_outdated_fw()
                # will need to re-pair
                devmgr = self.device_manager()

                def re_pair_device():
                    device_id = self.choose_device(window, keystore)
                    devmgr.unpair_id(device_id)
                    self.get_client(keystore)

                keystore.thread.add(re_pair_device)
            return
        else:
            window.on_error(exc_info)
Example #10
0
    def run_upgrades(self, storage: WalletStorage, db: 'WalletDB') -> None:
        path = storage.path
        if db.requires_split():
            self.hide()
            msg = _(
                "The wallet '{}' contains multiple accounts, which are no longer supported since Firo 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 = db.split_accounts(path)
            msg = _('Your accounts have been moved to') + ':\n' + '\n'.join(
                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 = db.get_action()
        if action and db.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 = json.loads(storage.read())
            self.run(action)
            for k, v in self.data.items():
                db.put(k, v)
            db.write(storage)
            return

        if db.requires_upgrade():
            self.upgrade_db(storage, db)
Example #11
0
 def file_input(self):
     fileName = getOpenFileName(
         parent=self,
         title='select file',
         config=self.config,
     )
     if not fileName:
         return
     try:
         try:
             with open(fileName, "r") as f:
                 data = f.read()
         except UnicodeError as e:
             with open(fileName, "rb") as f:
                 data = f.read()
             data = data.hex()
     except BaseException as e:
         self.show_error(_('Error opening file') + ':\n' + repr(e))
     else:
         self.setText(data)
Example #12
0
    def select_date(self, button):
        d = WindowModalDialog(self, _("Select date"))
        d.setMinimumSize(600, 150)
        d.date = None
        vbox = QVBoxLayout()

        def on_date(date):
            d.date = date

        cal = QCalendarWidget()
        cal.setGridVisible(True)
        cal.clicked[QDate].connect(on_date)
        vbox.addWidget(cal)
        vbox.addLayout(Buttons(OkButton(d), CancelButton(d)))
        d.setLayout(vbox)
        if d.exec_():
            if d.date is None:
                return None
            date = d.date.toPyDate()
            button.setText(self.format_date(date))
            return datetime.datetime(date.year, date.month, date.day)
Example #13
0
    def create_wallet_menu(self, position):
        a = self.w_cur_alias
        s = self.w_cur_state
        i = self.w_cur_idx
        if not i:
            return

        mn = self.manager.mns.get(a)
        owned = mn.is_owned
        operated = mn.is_operated
        protx_hash = mn.protx_hash
        removed = (s == WalletMNsModel.STATE_REMOVED)

        menu = QMenu()
        menu.addAction(_('Remove'), self.on_del_masternode)

        if not protx_hash:
            menu.addAction(_('Update Params'), self.on_update_params)

        if removed and owned:
            menu.addAction(_('Change Collateral'), self.on_update_collateral)

        if owned and not protx_hash:
            menu.addAction(_('Register Masternode'), self.on_make_pro_reg_tx)

        if operated and protx_hash and not removed:
            menu.addAction(_('Update Service'), self.on_make_pro_up_srv_tx)

        if owned and protx_hash and not removed:
            menu.addAction(_('Update Registrar'), self.on_make_pro_up_reg_tx)

        menu.addSeparator()
        menu.addAction(_('Rename Alias'),
                       lambda: self.w_view.edit(i))
        menu.addSeparator()
        menu.addAction(_('Details'), lambda: self.show_alias_info(a))
        menu.exec_(self.w_view.viewport().mapToGlobal(position))
Example #14
0
def address_field(addresses):
    hbox = QHBoxLayout()
    address_e = QLineEdit()
    if addresses and len(addresses) > 0:
        address_e.setText(addresses[0])
    else:
        addresses = []
    def func():
        try:
            i = addresses.index(str(address_e.text())) + 1
            i = i % len(addresses)
            address_e.setText(addresses[i])
        except ValueError:
            # the user might have changed address_e to an
            # address not in the wallet (or to something that isn't an address)
            if addresses and len(addresses) > 0:
                address_e.setText(addresses[0])
    button = QPushButton(_('Address'))
    button.clicked.connect(func)
    hbox.addWidget(button)
    hbox.addWidget(address_e)
    return hbox, address_e
Example #15
0
    def do_send(self, tx: Union[Transaction, PartialTransaction]):
        def on_success(result):
            window.show_message(
                _("Your transaction was sent to the cosigning pool.") + '\n' +
                _("Open your cosigner wallet to retrieve it."))

        def on_failure(exc_info):
            e = exc_info[1]
            try:
                self.logger.error("on_failure", exc_info=exc_info)
            except OSError:
                pass
            window.show_error(
                _("Failed to send transaction to cosigning pool") + ':\n' +
                repr(e))

        buffer = []
        some_window = None
        # construct messages
        for window, xpub, K, _hash in self.cosigner_list:
            if not self.cosigner_can_sign(tx, xpub):
                continue
            some_window = window
            raw_tx_bytes = tx.serialize_as_bytes()
            public_key = ecc.ECPubkey(K)
            message = public_key.encrypt_message(raw_tx_bytes).decode('ascii')
            buffer.append((_hash, message))
        if not buffer:
            return

        # send messages
        # note: we send all messages sequentially on the same thread
        def send_messages_task():
            for _hash, message in buffer:
                server.put(_hash, message)

        msg = _('Sending transaction to cosigning pool...')
        WaitingDialog(some_window, msg, send_messages_task, on_success,
                      on_failure)
Example #16
0
    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 = None  # type: Optional[Exception]
        res = None

        def task_wrapper():
            nonlocal exc
            nonlocal res
            try:
                res = task()
            except Exception as e:
                exc = e

        self.waiting_dialog(task_wrapper, msg=msg)
        if exc is None:
            return res
        else:
            raise exc
Example #17
0
    def __init__(self, msg, wallet=None):
        self.wallet = wallet

        vbox = QVBoxLayout()
        label = QLabel(msg + "\n")
        label.setWordWrap(True)

        grid = QGridLayout()
        grid.setSpacing(8)
        grid.setColumnMinimumWidth(0, 150)
        grid.setColumnMinimumWidth(1, 100)
        grid.setColumnStretch(1, 1)

        logo_grid = QGridLayout()
        logo_grid.setSpacing(8)
        logo_grid.setColumnMinimumWidth(0, 70)
        logo_grid.setColumnStretch(1, 1)

        logo = QLabel()
        logo.setAlignment(Qt.AlignCenter)

        logo_grid.addWidget(logo, 0, 0)
        logo_grid.addWidget(label, 0, 1, 1, 2)
        vbox.addLayout(logo_grid)

        if wallet and wallet.has_storage_encryption():
            lockfile = "lock.png"
        else:
            lockfile = "unlock.png"
        logo.setPixmap(
            QPixmap(icon_path(lockfile)).scaledToWidth(
                36, mode=Qt.SmoothTransformation))

        vbox.addLayout(grid)

        self.encrypt_cb = QCheckBox(_('Encrypt wallet file'))
        grid.addWidget(self.encrypt_cb, 1, 0, 1, 2)

        self.vbox = vbox
Example #18
0
    def _on_camera_status_changed(self, status: QCamera.Status):
        if self._ok_done:
            # camera/scan is quitting, abort.
            return

        self.logger.info('camera status changed to {}'.format(
            self._get_camera_status_name(status)))

        if status == QCamera.LoadedStatus:
            # Determine the optimal resolution and compute the crop rect
            camera_resolutions = self.camera.supportedViewfinderResolutions()
            try:
                resolution, was_ideal = self._get_resolution(
                    camera_resolutions, self.SCAN_SIZE)
            except RuntimeError as e:
                self._error_message = str(e)
                self.reject()
                return
            self._set_resolution(resolution)

            # Set the camera resolution
            viewfinder_settings = QCameraViewfinderSettings()
            viewfinder_settings.setResolution(resolution)
            self.camera.setViewfinderSettings(viewfinder_settings)

            # Counter for the QR scanner frame number
            self.frame_id = 0

            self.camera.start()
            self.lowres_label.setVisible(
                not was_ideal
            )  # if they have a low res camera, show the warning label.
        elif status == QCamera.UnloadedStatus or status == QCamera.UnavailableStatus:
            self._error_message = _(
                "Cannot start QR scanner, camera is unavailable.")
            self.reject()
        elif status == QCamera.ActiveStatus:
            self.open()
Example #19
0
    def on_receive(self, keyhash, message):
        self.logger.info(f"signal arrived for {keyhash}")
        for key, _hash, window in self.keys:
            if _hash == keyhash:
                break
        else:
            self.logger.info("keyhash not found")
            return

        wallet = window.wallet
        if isinstance(wallet.keystore, keystore.Hardware_KeyStore):
            window.show_warning(_('An encrypted transaction was retrieved from cosigning pool.') + '\n' +
                                _('However, hardware wallets do not support message decryption, '
                                  'which makes them not compatible with the current design of cosigner pool.'))
            return
        elif wallet.has_keystore_encryption():
            password = window.password_dialog(_('An encrypted transaction was retrieved from cosigning pool.') + '\n' +
                                              _('Please enter your password to decrypt it.'))
            if not password:
                return
        else:
            password = None
            if not window.question(_("An encrypted transaction was retrieved from cosigning pool.") + '\n' +
                                   _("Do you want to open it now?")):
                return

        xprv = wallet.keystore.get_master_private_key(password)
        if not xprv:
            return
        try:
            privkey = BIP32Node.from_xkey(xprv).eckey
            message = privkey.decrypt_message(message)
        except Exception as e:
            self.logger.exception('')
            window.show_error(_('Error decrypting message') + ':\n' + repr(e))
            return

        self.listener.clear(keyhash)
        tx = tx_from_any(message)
        show_transaction(tx, parent=window, prompt_if_unsaved=True)
Example #20
0
 def _start_wizard_to_select_or_create_wallet(
         self, path) -> Optional[Abstract_Wallet]:
     wizard = InstallWizard(self.config,
                            self.app,
                            self.plugins,
                            gui_object=self)
     try:
         path, storage = wizard.select_storage(path, self.daemon.get_wallet)
         # storage is None if file does not exist
         if storage is None:
             wizard.path = path  # needed by trustedcoin plugin
             wizard.run('new')
             storage, db = wizard.create_storage(path)
         else:
             db = WalletDB(storage.read(), manual_upgrades=False)
             if db.upgrade_done:
                 storage.backup_old_version()
             wizard.run_upgrades(storage, db)
         if getattr(storage, 'backup_message', None):
             custom_message_box(icon=QMessageBox.Warning,
                                parent=None,
                                title=_('Information'),
                                text=storage.backup_message)
             storage.backup_message = ''
     except (UserCancelled, GoBack):
         return
     except WalletAlreadyOpenInMemory as e:
         return e.wallet
     finally:
         wizard.terminate()
     # return if wallet creation is not complete
     if storage is None or db.get_action():
         return
     wallet = Wallet(db, storage, config=self.config)
     wallet.start_network(self.daemon.network)
     self.daemon.add_wallet(wallet)
     return wallet
Example #21
0
 def update(self):
     # not calling maybe_defer_update() as it interferes with conditional-visibility
     self.proxy.setDynamicSortFilter(
         False)  # temp. disable re-sorting after every change
     self.std_model.clear()
     self.update_headers(self.__class__.headers)
     for idx, item in enumerate(self.parent.wallet.get_invoices()):
         key = item.id
         icon_name = 'firocoin.png'
         if item.bip70:
             icon_name = 'seal.png'
         status = self.parent.wallet.get_invoice_status(item)
         status_str = item.get_status_str(status)
         message = item.message
         amount = item.get_amount_sat()
         timestamp = item.time or 0
         date_str = format_time(timestamp) if timestamp else _('Unknown')
         amount_str = self.parent.format_amount(amount, whitespaces=True)
         labels = [date_str, message, amount_str, status_str]
         items = [QStandardItem(e) for e in labels]
         self.set_editability(items)
         items[self.Columns.DATE].setIcon(read_QIcon(icon_name))
         items[self.Columns.STATUS].setIcon(read_QIcon(
             pr_icons.get(status)))
         items[self.Columns.DATE].setData(key, role=ROLE_REQUEST_ID)
         items[self.Columns.DATE].setData(item.type, role=ROLE_REQUEST_TYPE)
         items[self.Columns.DATE].setData(timestamp, role=ROLE_SORT_ORDER)
         self.std_model.insertRow(idx, items)
     self.filter()
     self.proxy.setDynamicSortFilter(True)
     # sort requests by date
     self.sortByColumn(self.Columns.DATE, Qt.DescendingOrder)
     # hide list if empty
     if self.parent.isVisible():
         b = self.std_model.rowCount() > 0
         self.setVisible(b)
         self.parent.invoices_label.setVisible(b)
Example #22
0
    def on_edit(self):
        s = self.get_seed()
        b = self.is_seed(s)
        if not self.is_bip39:
            t = seed_type(s)
            label = _('Seed Type') + ': ' + t if t else ''
        else:
            from electrum_firo.keystore import bip39_is_checksum_valid
            is_checksum, is_wordlist = bip39_is_checksum_valid(s)
            status = ('checksum: ' + ('ok' if is_checksum else 'failed')
                      ) if is_wordlist else 'unknown wordlist'
            label = 'BIP39' + ' (%s)' % status
        self.seed_type_label.setText(label)
        if self.on_edit_cb:
            self.on_edit_cb(b)
        else:
            self.parent.next_button.setEnabled(b)

        # disable suggestions if user already typed an unknown word
        for word in self.get_seed().split(" ")[:-1]:
            if word not in self.wordlist:
                self.seed_e.disable_suggestions()
                return
        self.seed_e.enable_suggestions()
Example #23
0
 def update_view(self, latest_version=None):
     if latest_version:
         self.pb.hide()
         self.latest_version_label.setText(
             _("Latest version: {}".format(latest_version)))
         if self.is_newer(latest_version):
             self.heading_label.setText(
                 '<h2>' + _("There is a new update available") + '</h2>')
             url = "<a href='{u}'>{u}</a>".format(
                 u=UpdateCheck.download_url)
             self.detail_label.setText(
                 _("You can download the new version from {}.").format(url))
         else:
             self.heading_label.setText('<h2>' + _("Already up to date") +
                                        '</h2>')
             self.detail_label.setText(
                 _("You are already on the latest version of Firo Electrum."
                   ))
     else:
         self.heading_label.setText('<h2>' + _("Checking for updates...") +
                                    '</h2>')
         self.detail_label.setText(
             _("Please wait while Firo Electrum checks for available updates."
               ))
Example #24
0
    def initialize_device(self, device_id, wizard, handler):
        # Initialization method
        msg = _("Choose how you want to initialize your {}.\n\n"
                "The first two methods are secure as no secret information "
                "is entered into your computer.\n\n"
                "For the last two methods you input secrets on your keyboard "
                "and upload them to your {}, and so you should "
                "only do those on a computer you know to be trustworthy "
                "and free of malware.").format(self.device, self.device)
        choices = [
            # Must be short as QT doesn't word-wrap radio button text
            (TIM_NEW,
             _("Let the device generate a completely new seed randomly")),
            (TIM_RECOVER,
             _("Recover from a seed you have previously written down")),
            (TIM_MNEMONIC, _("Upload a BIP39 mnemonic to generate the seed")),
            (TIM_PRIVKEY, _("Upload a master private key"))
        ]

        def f(method):
            import threading
            settings = self.request_trezor_init_settings(
                wizard, method, self.device)
            t = threading.Thread(target=self._initialize_device_safe,
                                 args=(settings, method, device_id, wizard,
                                       handler))
            t.setDaemon(True)
            t.start()
            exit_code = wizard.loop.exec_()
            if exit_code != 0:
                # this method (initialize_device) was called with the expectation
                # of leaving the device in an initialized state when finishing.
                # signal that this is not the case:
                raise UserCancelled()

        wizard.choice_dialog(title=_('Initialize Device'),
                             message=msg,
                             choices=choices,
                             run_next=f)
 def change_ps_ks_password_dialog(self):
     psman = self.psman
     d = PSKsPasswordDlg(self, self.wallet)
     ok, old_password, new_password = d.run()
     if not ok:
         return
     try:
         psman.update_ps_ks_password(old_password, new_password)
     except InvalidPassword as e:
         self.show_error(str(e))
         return
     except BaseException:
         self.logger.exception('Failed to update PS Keystore password')
         self.show_error(_('Failed to update PS Keystore password'))
         return
     if psman.is_ps_ks_encrypted():
         msg = _('Password was updated successfully')
         pwd_btn_text = _('Change Password')
     else:
         msg = _('Password is disabled, this wallet is not protected')
         pwd_btn_text = _('Set Password')
     self.show_message(msg, title=_("Success"))
     self.password_btn.setText(pwd_btn_text)
Example #26
0
    def request_safe_t_init_settings(self, wizard, method, device):
        vbox = QVBoxLayout()
        next_enabled = True
        label = QLabel(_("Enter a label to name your device:"))
        name = QLineEdit()
        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addWidget(name)
        hl.addStretch(1)
        vbox.addLayout(hl)

        def clean_text(widget):
            text = widget.toPlainText().strip()
            return ' '.join(text.split())

        if method in [TIM_NEW, TIM_RECOVER]:
            gb = QGroupBox()
            hbox1 = QHBoxLayout()
            gb.setLayout(hbox1)
            vbox.addWidget(gb)
            gb.setTitle(_("Select your seed length:"))
            bg = QButtonGroup()
            for i, count in enumerate([12, 18, 24]):
                rb = QRadioButton(gb)
                rb.setText(_("{:d} words").format(count))
                bg.addButton(rb)
                bg.setId(rb, i)
                hbox1.addWidget(rb)
                rb.setChecked(True)
            cb_pin = QCheckBox(_('Enable PIN protection'))
            cb_pin.setChecked(True)
        else:
            text = QTextEdit()
            text.setMaximumHeight(60)
            if method == TIM_MNEMONIC:
                msg = _("Enter your BIP39 mnemonic:")
            else:
                msg = _("Enter the master private key beginning with xprv:")
                def set_enabled():
                    from electrum_firo.bip32 import is_xprv
                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))
                text.textChanged.connect(set_enabled)
                next_enabled = False

            vbox.addWidget(QLabel(msg))
            vbox.addWidget(text)
            pin = QLineEdit()
            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
            pin.setMaximumWidth(100)
            hbox_pin = QHBoxLayout()
            hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
            hbox_pin.addWidget(pin)
            hbox_pin.addStretch(1)

        if method in [TIM_NEW, TIM_RECOVER]:
            vbox.addWidget(WWLabel(RECOMMEND_PIN))
            vbox.addWidget(cb_pin)
        else:
            vbox.addLayout(hbox_pin)

        passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        cb_phrase = QCheckBox(_('Enable passphrases'))
        cb_phrase.setChecked(False)
        vbox.addWidget(passphrase_msg)
        vbox.addWidget(passphrase_warning)
        vbox.addWidget(cb_phrase)

        wizard.exec_layout(vbox, next_enabled=next_enabled)

        if method in [TIM_NEW, TIM_RECOVER]:
            item = bg.checkedId()
            pin = cb_pin.isChecked()
        else:
            item = ' '.join(str(clean_text(text)).split())
            pin = str(pin.text())

        return (item, name.text(), pin, cb_phrase.isChecked())
Example #27
0
 def slider_moved():
     mins = timeout_slider.sliderPosition()
     timeout_minutes.setText(_("{:2d} minutes").format(mins))
Example #28
0
    def __init__(self, window, plugin, keystore, device_id):
        title = _("{} Settings").format(plugin.device)
        super(SettingsDialog, self).__init__(window, title)
        self.setMaximumWidth(540)

        devmgr = plugin.device_manager()
        config = devmgr.config
        handler = keystore.handler
        thread = keystore.thread
        hs_cols, hs_rows = (128, 64)

        def invoke_client(method, *args, **kw_args):
            unpair_after = kw_args.pop('unpair_after', False)

            def task():
                client = devmgr.client_by_id(device_id)
                if not client:
                    raise RuntimeError("Device not connected")
                if method:
                    getattr(client, method)(*args, **kw_args)
                if unpair_after:
                    devmgr.unpair_id(device_id)
                return client.features

            thread.add(task, on_success=update)

        def update(features):
            self.features = features
            set_label_enabled()
            if features.bootloader_hash:
                bl_hash = bh2u(features.bootloader_hash)
                bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]])
            else:
                bl_hash = "N/A"
            noyes = [_("No"), _("Yes")]
            endis = [_("Enable Passphrases"), _("Disable Passphrases")]
            disen = [_("Disabled"), _("Enabled")]
            setchange = [_("Set a PIN"), _("Change PIN")]

            version = "%d.%d.%d" % (features.major_version,
                                    features.minor_version,
                                    features.patch_version)

            device_label.setText(features.label)
            pin_set_label.setText(noyes[features.pin_protection])
            passphrases_label.setText(disen[features.passphrase_protection])
            bl_hash_label.setText(bl_hash)
            label_edit.setText(features.label)
            device_id_label.setText(features.device_id)
            initialized_label.setText(noyes[features.initialized])
            version_label.setText(version)
            clear_pin_button.setVisible(features.pin_protection)
            clear_pin_warning.setVisible(features.pin_protection)
            pin_button.setText(setchange[features.pin_protection])
            pin_msg.setVisible(not features.pin_protection)
            passphrase_button.setText(endis[features.passphrase_protection])
            language_label.setText(features.language)

        def set_label_enabled():
            label_apply.setEnabled(label_edit.text() != self.features.label)

        def rename():
            invoke_client('change_label', label_edit.text())

        def toggle_passphrase():
            title = _("Confirm Toggle Passphrase Protection")
            currently_enabled = self.features.passphrase_protection
            if currently_enabled:
                msg = _("After disabling passphrases, you can only pair this "
                        "Firo Electrum wallet if it had an empty passphrase.  "
                        "If its passphrase was not empty, you will need to "
                        "create a new wallet with the install wizard.  You "
                        "can use this wallet again at any time by re-enabling "
                        "passphrases and entering its passphrase.")
            else:
                msg = _("Your current Firo Electrum wallet can only be used with "
                        "an empty passphrase.  You must create a separate "
                        "wallet with the install wizard for other passphrases "
                        "as each one generates a new set of addresses.")
            msg += "\n\n" + _("Are you sure you want to proceed?")
            if not self.question(msg, title=title):
                return
            invoke_client('toggle_passphrase', unpair_after=currently_enabled)

        def change_homescreen():
            filename = getOpenFileName(
                parent=self,
                title=_("Choose Homescreen"),
                config=config,
            )
            if not filename:
                return  # user cancelled

            if filename.endswith('.toif'):
                img = open(filename, 'rb').read()
                if img[:8] != b'TOIf\x90\x00\x90\x00':
                    handler.show_error('File is not a TOIF file with size of 144x144')
                    return
            else:
                from PIL import Image # FIXME
                im = Image.open(filename)
                if im.size != (128, 64):
                    handler.show_error('Image must be 128 x 64 pixels')
                    return
                im = im.convert('1')
                pix = im.load()
                img = bytearray(1024)
                for j in range(64):
                    for i in range(128):
                        if pix[i, j]:
                            o = (i + j * 128)
                            img[o // 8] |= (1 << (7 - o % 8))
                img = bytes(img)
            invoke_client('change_homescreen', img)

        def clear_homescreen():
            invoke_client('change_homescreen', b'\x00')

        def set_pin():
            invoke_client('set_pin', remove=False)

        def clear_pin():
            invoke_client('set_pin', remove=True)

        def wipe_device():
            wallet = window.wallet
            if wallet and sum(wallet.get_balance()):
                title = _("Confirm Device Wipe")
                msg = _("Are you SURE you want to wipe the device?\n"
                        "Your wallet still has Dash coins in it!")
                if not self.question(msg, title=title,
                                     icon=QMessageBox.Critical):
                    return
            invoke_client('wipe_device', unpair_after=True)

        def slider_moved():
            mins = timeout_slider.sliderPosition()
            timeout_minutes.setText(_("{:2d} minutes").format(mins))

        def slider_released():
            config.set_session_timeout(timeout_slider.sliderPosition() * 60)

        # Information tab
        info_tab = QWidget()
        info_layout = QVBoxLayout(info_tab)
        info_glayout = QGridLayout()
        info_glayout.setColumnStretch(2, 1)
        device_label = QLabel()
        pin_set_label = QLabel()
        passphrases_label = QLabel()
        version_label = QLabel()
        device_id_label = QLabel()
        bl_hash_label = QLabel()
        bl_hash_label.setWordWrap(True)
        language_label = QLabel()
        initialized_label = QLabel()
        rows = [
            (_("Device Label"), device_label),
            (_("PIN set"), pin_set_label),
            (_("Passphrases"), passphrases_label),
            (_("Firmware Version"), version_label),
            (_("Device ID"), device_id_label),
            (_("Bootloader Hash"), bl_hash_label),
            (_("Language"), language_label),
            (_("Initialized"), initialized_label),
        ]
        for row_num, (label, widget) in enumerate(rows):
            info_glayout.addWidget(QLabel(label), row_num, 0)
            info_glayout.addWidget(widget, row_num, 1)
        info_layout.addLayout(info_glayout)

        # Settings tab
        settings_tab = QWidget()
        settings_layout = QVBoxLayout(settings_tab)
        settings_glayout = QGridLayout()

        # Settings tab - Label
        label_msg = QLabel(_("Name this {}.  If you have multiple devices "
                             "their labels help distinguish them.")
                           .format(plugin.device))
        label_msg.setWordWrap(True)
        label_label = QLabel(_("Device Label"))
        label_edit = QLineEdit()
        label_edit.setMinimumWidth(150)
        label_edit.setMaxLength(plugin.MAX_LABEL_LEN)
        label_apply = QPushButton(_("Apply"))
        label_apply.clicked.connect(rename)
        label_edit.textChanged.connect(set_label_enabled)
        settings_glayout.addWidget(label_label, 0, 0)
        settings_glayout.addWidget(label_edit, 0, 1, 1, 2)
        settings_glayout.addWidget(label_apply, 0, 3)
        settings_glayout.addWidget(label_msg, 1, 1, 1, -1)

        # Settings tab - PIN
        pin_label = QLabel(_("PIN Protection"))
        pin_button = QPushButton()
        pin_button.clicked.connect(set_pin)
        settings_glayout.addWidget(pin_label, 2, 0)
        settings_glayout.addWidget(pin_button, 2, 1)
        pin_msg = QLabel(_("PIN protection is strongly recommended.  "
                           "A PIN is your only protection against someone "
                           "stealing your Dash coins if they obtain physical "
                           "access to your {}.").format(plugin.device))
        pin_msg.setWordWrap(True)
        pin_msg.setStyleSheet("color: red")
        settings_glayout.addWidget(pin_msg, 3, 1, 1, -1)

        # Settings tab - Homescreen
        homescreen_label = QLabel(_("Homescreen"))
        homescreen_change_button = QPushButton(_("Change..."))
        homescreen_clear_button = QPushButton(_("Reset"))
        homescreen_change_button.clicked.connect(change_homescreen)
        try:
            import PIL
        except ImportError:
            homescreen_change_button.setDisabled(True)
            homescreen_change_button.setToolTip(
                _("Required package 'PIL' is not available - Please install it.")
            )
        homescreen_clear_button.clicked.connect(clear_homescreen)
        homescreen_msg = QLabel(_("You can set the homescreen on your "
                                  "device to personalize it.  You must "
                                  "choose a {} x {} monochrome black and "
                                  "white image.").format(hs_cols, hs_rows))
        homescreen_msg.setWordWrap(True)
        settings_glayout.addWidget(homescreen_label, 4, 0)
        settings_glayout.addWidget(homescreen_change_button, 4, 1)
        settings_glayout.addWidget(homescreen_clear_button, 4, 2)
        settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1)

        # Settings tab - Session Timeout
        timeout_label = QLabel(_("Session Timeout"))
        timeout_minutes = QLabel()
        timeout_slider = QSlider(Qt.Horizontal)
        timeout_slider.setRange(1, 60)
        timeout_slider.setSingleStep(1)
        timeout_slider.setTickInterval(5)
        timeout_slider.setTickPosition(QSlider.TicksBelow)
        timeout_slider.setTracking(True)
        timeout_msg = QLabel(
            _("Clear the session after the specified period "
              "of inactivity.  Once a session has timed out, "
              "your PIN and passphrase (if enabled) must be "
              "re-entered to use the device."))
        timeout_msg.setWordWrap(True)
        timeout_slider.setSliderPosition(config.get_session_timeout() // 60)
        slider_moved()
        timeout_slider.valueChanged.connect(slider_moved)
        timeout_slider.sliderReleased.connect(slider_released)
        settings_glayout.addWidget(timeout_label, 6, 0)
        settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3)
        settings_glayout.addWidget(timeout_minutes, 6, 4)
        settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1)
        settings_layout.addLayout(settings_glayout)
        settings_layout.addStretch(1)

        # Advanced tab
        advanced_tab = QWidget()
        advanced_layout = QVBoxLayout(advanced_tab)
        advanced_glayout = QGridLayout()

        # Advanced tab - clear PIN
        clear_pin_button = QPushButton(_("Disable PIN"))
        clear_pin_button.clicked.connect(clear_pin)
        clear_pin_warning = QLabel(
            _("If you disable your PIN, anyone with physical access to your "
              "{} device can spend your Dash coins.").format(plugin.device))
        clear_pin_warning.setWordWrap(True)
        clear_pin_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(clear_pin_button, 0, 2)
        advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5)

        # Advanced tab - toggle passphrase protection
        passphrase_button = QPushButton()
        passphrase_button.clicked.connect(toggle_passphrase)
        passphrase_msg = WWLabel(PASSPHRASE_HELP)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(passphrase_button, 3, 2)
        advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5)
        advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5)

        # Advanced tab - wipe device
        wipe_device_button = QPushButton(_("Wipe Device"))
        wipe_device_button.clicked.connect(wipe_device)
        wipe_device_msg = QLabel(
            _("Wipe the device, removing all data from it.  The firmware "
              "is left unchanged."))
        wipe_device_msg.setWordWrap(True)
        wipe_device_warning = QLabel(
            _("Only wipe a device if you have the recovery seed written down "
              "and the device wallet(s) are empty, otherwise the Dash coins "
              "will be lost forever."))
        wipe_device_warning.setWordWrap(True)
        wipe_device_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(wipe_device_button, 6, 2)
        advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5)
        advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5)
        advanced_layout.addLayout(advanced_glayout)
        advanced_layout.addStretch(1)

        tabs = QTabWidget(self)
        tabs.addTab(info_tab, _("Information"))
        tabs.addTab(settings_tab, _("Settings"))
        tabs.addTab(advanced_tab, _("Advanced"))
        dialog_vbox = QVBoxLayout(self)
        dialog_vbox.addWidget(tabs)
        dialog_vbox.addLayout(Buttons(CloseButton(self)))

        # Update information
        invoke_client(None)
Example #29
0
                             QTextEdit, QLineEdit, QRadioButton, QCheckBox, QWidget,
                             QMessageBox, QFileDialog, QSlider, QTabWidget)

from electrum_firo.gui.qt.util import (WindowModalDialog, WWLabel, Buttons, CancelButton,
                                       OkButton, CloseButton, getOpenFileName)
from electrum_firo.i18n import _
from electrum_firo.plugin import hook
from electrum_firo.util import bh2u

from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
from ..hw_wallet.plugin import only_hook_if_libraries_available
from .safe_t import SafeTPlugin, TIM_NEW, TIM_RECOVER, TIM_MNEMONIC


PASSPHRASE_HELP_SHORT =_(
    "Passphrases allow you to access new wallets, each "
    "hidden behind a particular case-sensitive passphrase.")
PASSPHRASE_HELP = PASSPHRASE_HELP_SHORT + "  " + _(
    "You need to create a separate Firo Electrum wallet for each passphrase "
    "you use as they each generate different addresses.  Changing "
    "your passphrase does not lose other wallets, each is still "
    "accessible behind its own passphrase.")
RECOMMEND_PIN = _(
    "You should enable PIN protection.  Your PIN is the only protection "
    "for your Dash coins if your device is lost or stolen.")
PASSPHRASE_NOT_PIN = _(
    "If you forget a passphrase you will be unable to access any "
    "Dash coins in the wallet behind it.  A passphrase is not a PIN. "
    "Only change this if you are sure you understand it.")

Example #30
0
from electrum_firo.i18n import _

fullname = 'Ledger Wallet'
description = 'Provides support for Ledger hardware wallet'
requires = [('btchip', 'github.com/ledgerhq/btchip-python')]
registers_keystore = ('hardware', 'ledger', _("Ledger wallet"))
available_for = ['qt', 'cmdline']