def on_otp(self, wallet, tx, otp, on_success, on_failure): try: wallet.on_otp(tx, otp) except TrustedCoinException as e: if e.status_code == 400: # invalid OTP Clock.schedule_once( lambda dt: on_failure(_('Invalid one-time password.'))) else: Clock.schedule_once(lambda dt, bound_e=e: on_failure( _('Error') + ':\n' + str(bound_e))) except Exception as e: Clock.schedule_once(lambda dt, bound_e=e: on_failure( _('Error') + ':\n' + str(bound_e))) else: on_success(tx)
def create_menu(self, position): item = self.currentItem() if not item: return is_server = not bool(item.data(0, Qt.UserRole)) menu = QMenu() if is_server: server = item.data(1, Qt.UserRole) menu.addAction(_("Use as server"), lambda: self.parent.follow_server(server)) else: chain_id = item.data(1, Qt.UserRole) menu.addAction(_("Follow this branch"), lambda: self.parent.follow_branch(chain_id)) menu.exec_(self.viewport().mapToGlobal(position))
def purge_transaction(self): mods = ['_pick', '_signed', '_lock', '_shutdown'] if not self.window.question( _("Purging you transactions will erase the current transaction" ) + '\n' + _("Are you sure you want to purge your transaction?")): return for mod in mods: for key, _hash, window in self.keys: server.delete(_hash) server.delete(_hash + mod) for window, xpub, K, _hash in self.cosigner_list: server.delete(_hash) server.delete(_hash + mod) self.window.show_message(_("Your transactions have been purged."))
def __init__(self, parent=None, msg=None): msg = msg or _('Please enter your password') WindowModalDialog.__init__(self, parent, _("Enter Password")) self.pw = pw = QLineEdit() pw.setEchoMode(2) vbox = QVBoxLayout() vbox.addWidget(QLabel(msg)) grid = QGridLayout() grid.setSpacing(8) grid.addWidget(QLabel(_('Password')), 1, 0) grid.addWidget(pw, 1, 1) vbox.addLayout(grid) vbox.addLayout(Buttons(CancelButton(self), OkButton(self))) self.setLayout(vbox) run_hook('password_dialog', pw, grid, 1)
def update(self): inv_list = self.parent.invoices.unpaid_invoices() self.model().clear() self.update_headers(self.__class__.headers) self.header().setSectionResizeMode(self.Columns.REQUESTOR, QHeaderView.Interactive) for idx, pr in enumerate(inv_list): key = pr.get_id() status = self.parent.invoices.get_status(key) if status is None: continue requestor = pr.get_requestor() exp = pr.get_expiration_date() date_str = format_time(exp) if exp else _('Never') labels = [date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')] items = [QStandardItem(e) for e in labels] self.set_editability(items) items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) items[self.Columns.DATE].setData(key, role=Qt.UserRole) items[self.Columns.REQUESTOR].setFont(QFont(MONOSPACE_FONT)) items[self.Columns.AMOUNT].setFont(QFont(MONOSPACE_FONT)) self.model().insertRow(idx, items) self.selectionModel().select(self.model().index(0,0), QItemSelectionModel.SelectCurrent) if self.parent.isVisible(): b = len(inv_list) > 0 self.setVisible(b) self.parent.invoices_label.setVisible(b) self.filter()
def accept_terms_of_use(self, wizard): def handle_error(msg, e): wizard.show_error(msg + ':\n' + str(e)) wizard.terminate() try: tos = server.get_terms_of_service() except ErrorConnectingServer as e: Clock.schedule_once(lambda dt, bound_e=e: handle_error( _('Error connecting to server'), bound_e)) except Exception as e: Clock.schedule_once( lambda dt, bound_e=e: handle_error(_('Error'), bound_e)) else: f = lambda x: self.read_email(wizard) wizard.tos_dialog(tos=tos, run_next=f)
def prompt_user_for_otp(self, wallet, tx, on_success, on_failure): from ...gui.kivy.uix.dialogs.label_dialog import LabelDialog msg = _('Please enter your Google Authenticator code') d = LabelDialog( msg, '', lambda otp: self.on_otp(wallet, tx, otp, on_success, on_failure)) d.open()
def get_pin(self, code=None): if code == 2: msg = _("Enter a new PIN for your {}:") elif code == 3: msg = (_("Re-enter the new PIN for your {}.\n\n" "NOTE: the positions of the numbers have changed!")) else: msg = _("Enter your current {} PIN:") pin = self.handler.get_pin(msg.format(self.device)) if not pin: raise Cancelled if len(pin) > 9: self.handler.show_error( _('The PIN cannot be longer than 9 characters.')) raise Cancelled return pin
def send(self, window, addr): from electrum_exos import paymentrequest r = window.wallet.receive_requests.get(addr) message = r.get('memo', '') if r.get('signature'): pr = paymentrequest.serialize_request(r) else: pr = paymentrequest.make_request(self.config, r) if not pr: return recipient, ok = QInputDialog.getText(window, 'Send request', 'Email invoice to:') if not ok: return recipient = str(recipient) payload = pr.SerializeToString() self.print_error('sending mail to', recipient) try: # FIXME this runs in the GUI thread and blocks it... self.processor.send(recipient, message, payload) except BaseException as e: traceback.print_exc(file=sys.stderr) window.show_message(str(e)) else: window.show_message(_('Request sent.'))
def callback_WordRequest(self, msg): self.step += 1 msg = _("Step {}/24. Enter seed word as explained on " "your {}:").format(self.step, self.device) word = self.handler.get_word(msg) # Unfortunately the device can't handle self.proto.Cancel() return self.proto.WordAck(word=word)
def restore_wallet(self, wizard): wizard.opt_bip39 = False wizard.opt_ext = True title = _("Restore two-factor Wallet") f = lambda seed, is_bip39, is_ext: wizard.run('on_restore_seed', seed, is_ext) wizard.restore_seed_dialog(run_next=f, test=self.is_valid_seed)
def _initialize_device(self, settings, method, device_id, wizard, handler): item, label, pin_protection, passphrase_protection = settings language = 'english' devmgr = self.device_manager() client = devmgr.client_by_id(device_id) if not client: raise Exception(_("The device was disconnected.")) if method == TIM_NEW: strength = 64 * (item + 2) # 128, 192 or 256 client.reset_device(True, strength, passphrase_protection, pin_protection, label, language) elif method == TIM_RECOVER: word_count = 6 * (item + 2) # 12, 18 or 24 client.step = 0 client.recovery_device(word_count, passphrase_protection, pin_protection, label, language) elif method == TIM_MNEMONIC: pin = pin_protection # It's the pin, not a boolean client.load_device_by_mnemonic(str(item), pin, passphrase_protection, label, language) else: pin = pin_protection # It's the pin, not a boolean client.load_device_by_xprv(item, pin, passphrase_protection, label, language)
def show_address(self, wallet, address, keystore=None): if keystore is None: keystore = wallet.get_keystore() if not self.show_address_helper(wallet, address, keystore): return client = self.get_client(keystore) if not client.atleast_version(1, 3): keystore.handler.show_error(_("Your device firmware is too old")) return change, index = wallet.get_address_index(address) derivation = keystore.derivation address_path = "%s/%d/%d"%(derivation, change, index) address_n = client.expand_path(address_path) xpubs = wallet.get_master_public_keys() if len(xpubs) == 1: script_type = self.get_keepkey_input_script_type(wallet.txin_type) client.get_address(self.get_coin_name(), address_n, True, script_type=script_type) else: def f(xpub): return self._make_node_path(xpub, [change, index]) pubkeys = wallet.get_public_keys(address) # sort xpubs using the order of pubkeys sorted_pubkeys, sorted_xpubs = zip(*sorted(zip(pubkeys, xpubs))) pubkeys = list(map(f, sorted_xpubs)) multisig = self.types.MultisigRedeemScriptType( pubkeys=pubkeys, signatures=[b''] * wallet.n, m=wallet.m, ) script_type = self.get_keepkey_input_script_type(wallet.txin_type) client.get_address(self.get_coin_name(), address_n, True, multisig=multisig, script_type=script_type)
def create_client(self, device, handler): try: self.print_error("connecting to device at", device.path) transport = self.transport_handler.get_transport(device.path) except BaseException as e: self.print_error("cannot connect at", device.path, str(e)) return None if not transport: self.print_error("cannot connect at", device.path) return self.print_error("connected to device at", device.path) client = self.client_class(transport, handler, self) # Try a ping for device sanity try: client.ping('t') except BaseException as e: self.print_error("ping failed", str(e)) return None if not client.atleast_version(*self.minimum_firmware): msg = (_('Outdated {} firmware for device labelled {}. Please ' 'download the updated firmware from {}').format( self.device, client.label(), self.firmware_URL)) self.print_error(msg) if handler: handler.show_error(msg) else: raise UserFacingException(msg) return None return client
def create_client(self, device, handler): if device.product_key[1] == 2: transport = self._try_webusb(device) else: transport = self._try_hid(device) if not transport: self.logger.info("cannot connect to device") return self.logger.info(f"connected to device at {device.path}") client = self.client_class(transport, handler, self) # Try a ping for device sanity try: client.ping('t') except BaseException as e: self.logger.info(f"ping failed {e}") return None if not client.atleast_version(*self.minimum_firmware): msg = (_('Outdated {} firmware for device labelled {}. Please ' 'download the updated firmware from {}') .format(self.device, client.label(), self.firmware_URL)) self.logger.info(msg) if handler: handler.show_error(msg) else: raise UserFacingException(msg) return None return client
def passphrase_dialog(self, msg, confirm): # If confirm is true, require the user to enter the passphrase twice parent = self.top_level_window() d = WindowModalDialog(parent, _("Enter Passphrase")) if confirm: OK_button = OkButton(d) playout = PasswordLayout(msg=msg, kind=PW_PASSPHRASE, OK_button=OK_button) vbox = QVBoxLayout() vbox.addLayout(playout.layout()) vbox.addLayout(Buttons(CancelButton(d), OK_button)) d.setLayout(vbox) passphrase = playout.new_password() if d.exec_() else None else: pw = QLineEdit() pw.setEchoMode(2) pw.setMinimumWidth(200) vbox = QVBoxLayout() vbox.addWidget(WWLabel(msg)) vbox.addWidget(pw) vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) d.setLayout(vbox) passphrase = pw.text() if d.exec_() else None self.passphrase = passphrase self.done.set()
def __init__(self, text=None): ButtonsTextEdit.__init__(self, text) self.setReadOnly(1) icon = "qrcode_white.png" if ColorScheme.dark_scheme else "qrcode.png" self.addButton(icon, self.qr_show, _("Show as QR code")) run_hook('show_text_edit', self)
def dbb_has_password(self): reply = self.hid_send_plain(b'{"ping":""}') if 'ping' not in reply: raise UserFacingException(_('Device communication error. Please unplug and replug your Digital Bitbox.')) if reply['ping'] == 'password': return True return False
def seed_device_dialog(self): msg = _("Choose how to initialize your Digital Bitbox:") + "\n" choices = [ (_("Generate a new random wallet")), (_("Load a wallet from the micro SD card")) ] try: reply = self.handler.win.query_choice(msg, choices) except Exception: return # Back button pushed if reply == 0: self.dbb_generate_wallet() else: if not self.dbb_load_backup(show_msg=False): return self.isInitialized = True
def callback_PinMatrixRequest(self, msg): if msg.type == 2: msg = _("Enter a new PIN for your {}:") elif msg.type == 3: msg = (_("Re-enter the new PIN for your {}.\n\n" "NOTE: the positions of the numbers have changed!")) else: msg = _("Enter your current {} PIN:") pin = self.handler.get_pin(msg.format(self.device)) if len(pin) > 9: self.handler.show_error( _('The PIN cannot be longer than 9 characters.')) pin = '' # to cancel below if not pin: return self.proto.Cancel() return self.proto.PinMatrixAck(pin=pin)
def _initialize_device(self, settings, method, device_id, wizard, handler): item, label, pin_protection, passphrase_protection, recovery_type = settings if method == TIM_RECOVER and recovery_type == RECOVERY_TYPE_SCRAMBLED_WORDS: handler.show_error(_( "You will be asked to enter 24 words regardless of your " "seed's actual length. If you enter a word incorrectly or " "misspell it, you cannot change it or go back - you will need " "to start again from the beginning.\n\nSo please enter " "the words carefully!"), blocking=True) devmgr = self.device_manager() client = devmgr.client_by_id(device_id) if method == TIM_NEW: client.reset_device( strength=64 * (item + 2), # 128, 192 or 256 passphrase_protection=passphrase_protection, pin_protection=pin_protection, label=label) elif method == TIM_RECOVER: client.recover_device( recovery_type=recovery_type, word_count=6 * (item + 2), # 12, 18 or 24 passphrase_protection=passphrase_protection, pin_protection=pin_protection, label=label) if recovery_type == RECOVERY_TYPE_MATRIX: handler.close_matrix_dialog() else: raise RuntimeError("Unsupported recovery method")
def check_libraries_available(self) -> bool: def version_str(t): return ".".join(str(i) for i in t) try: # this might raise ImportError or LibraryFoundButUnusable library_version = self.get_library_version() # if no exception so far, we might still raise LibraryFoundButUnusable if (library_version == 'unknown' or versiontuple(library_version) < self.minimum_library or hasattr(self, "maximum_library") and versiontuple(library_version) >= self.maximum_library): raise LibraryFoundButUnusable(library_version=library_version) except ImportError: return False except LibraryFoundButUnusable as e: library_version = e.library_version max_version_str = version_str(self.maximum_library) if hasattr(self, "maximum_library") else "inf" self.libraries_available_message = ( _("Library version for '{}' is incompatible.").format(self.name) + '\nInstalled: {}, Needed: {} <= x < {}' .format(library_version, version_str(self.minimum_library), max_version_str)) self.print_stderr(self.libraries_available_message) return False return True
def change_homescreen(): dialog = QFileDialog(self, _("Choose Homescreen")) filename, __ = dialog.getOpenFileName() 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 message_dialog(self, msg): self.clear_dialog() self.dialog = dialog = WindowModalDialog(self.top_level_window(), _("Ledger Status")) l = QLabel(msg) vbox = QVBoxLayout(dialog) vbox.addWidget(l) dialog.show()
def password_dialog(self, pw, grid, pos): vkb_button = QPushButton(_("+")) vkb_button.setFixedWidth(20) vkb_button.clicked.connect(lambda: self.toggle_vkb(grid, pw)) grid.addWidget(vkb_button, pos, 2) self.kb_pos = 2 self.vkb = None
def send_report(self): def on_success(response): # note: 'response' coming from (remote) crash reporter server. # It contains a URL to the GitHub issue, so we allow rich text. self.show_message(parent=self, title=_("Crash report"), msg=response, rich_text=True) self.close() def on_failure(exc_info): e = exc_info[1] self.logger.error( 'There was a problem with the automatic reporting', exc_info=exc_info) self.show_critical( parent=self, msg=( _('There was a problem with the automatic reporting:') + '<br/>' + repr(e)[:120] + '<br/>' + _("Please report this issue manually") + f' <a href="{constants.GIT_REPO_ISSUES_URL}">on GitHub</a>.' ), rich_text=True) proxy = self.main_window.network.proxy task = lambda: BaseCrashReporter.send_report( self, self.main_window.network.asyncio_loop, proxy) msg = _('Sending crash report...') WaitingDialog(self, msg, task, on_success, on_failure)
def settings_dialog(self, window): d = WindowModalDialog(window, _("Email settings")) d.setMinimumSize(500, 200) vbox = QVBoxLayout(d) vbox.addWidget(QLabel(_('Server hosting your email account'))) grid = QGridLayout() vbox.addLayout(grid) grid.addWidget(QLabel('Server (IMAP)'), 0, 0) server_e = QLineEdit() server_e.setText(self.imap_server) grid.addWidget(server_e, 0, 1) grid.addWidget(QLabel('Username'), 1, 0) username_e = QLineEdit() username_e.setText(self.username) grid.addWidget(username_e, 1, 1) grid.addWidget(QLabel('Password'), 2, 0) password_e = QLineEdit() password_e.setText(self.password) grid.addWidget(password_e, 2, 1) vbox.addStretch() vbox.addLayout(Buttons(CloseButton(d), OkButton(d))) if not d.exec_(): return server = str(server_e.text()) self.config.set_key('email_server', server) self.imap_server = server username = str(username_e.text()) self.config.set_key('email_username', username) self.username = username password = str(password_e.text()) self.config.set_key('email_password', password) self.password = password check_connection = CheckConnectionThread(server, username, password) check_connection.connection_error_signal.connect( lambda e: window.show_message( _("Unable to connect to mail server:\n {}").format(e) + "\n" + _("Please check your connection and credentials."))) check_connection.start()
def sign_message(self, sequence, message, password): message = message.encode('utf8') message_hash = hashlib.sha256(message).hexdigest().upper() # prompt for the PIN before displaying the dialog if necessary client = self.get_client() address_path = self.get_derivation()[2:] + "/%d/%d" % sequence self.handler.show_message("Signing message ...\r\nMessage hash: " + message_hash) try: info = self.get_client().signMessagePrepare(address_path, message) pin = "" if info['confirmationNeeded']: pin = self.handler.get_auth( info) # does the authenticate dialog and returns pin if not pin: raise UserWarning(_('Cancelled by user')) pin = str(pin).encode() signature = self.get_client().signMessageSign(pin) except BTChipException as e: if e.sw == 0x6a80: self.give_error( "Unfortunately, this message cannot be signed by the Ledger wallet. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry." ) elif e.sw == 0x6985: # cancelled by user return b'' elif e.sw == 0x6982: raise # pin lock. decorator will catch it else: self.give_error(e, True) except UserWarning: self.handler.show_error(_('Cancelled by user')) return b'' except Exception as e: self.give_error(e, True) finally: self.handler.finished() # Parse the ASN.1 signature rLength = signature[3] r = signature[4:4 + rLength] sLength = signature[4 + rLength + 1] s = signature[4 + rLength + 2:] if rLength == 33: r = r[1:] if sLength == 33: s = s[1:] # And convert it return bytes([27 + 4 + (signature[0] & 0x01)]) + r + s
def add_show_address_on_hw_device_button_for_receive_addr(self, wallet, keystore, main_window): plugin = keystore.plugin receive_address_e = main_window.receive_address_e def show_address(): addr = receive_address_e.text() keystore.thread.add(partial(plugin.show_address, wallet, addr, keystore)) receive_address_e.addButton("eye1.png", show_address, _("Show on {}").format(plugin.device))
def create_menu(self, position): item = self.currentItem() if not item: return menu = QMenu() server = item.data(self.Columns.HOST, self.SERVER_STR_ROLE) menu.addAction(_("Use as server"), lambda: self.set_server(server)) menu.exec_(self.viewport().mapToGlobal(position))