def dbb_erase(self): self.handler.show_message(_("Are you sure you want to erase the Digital Bitbox?\r\n\r\n" \ "To continue, touch the Digital Bitbox's light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the light or wait for the timeout.")) hid_reply = self.hid_send_encrypt(b'{"reset":"__ERASE__"}') self.handler.finished() if 'error' in hid_reply: raise Exception(hid_reply['error']['message']) else: self.password = None raise Exception('Device erased')
def __init__(self, parent, message, task, on_success=None, on_error=None): assert parent if isinstance(parent, MessageBoxMixin): parent = parent.top_level_window() WindowModalDialog.__init__(self, parent, _("Please wait")) vbox = QVBoxLayout(self) vbox.addWidget(QLabel(message)) self.accepted.connect(self.on_accepted) self.show() self.thread = TaskThread(self) self.thread.add(task, on_success, self.accept, on_error)
def get_xpub(self, device_id, derivation, xtype, wizard): if xtype not in ('standard', 'p2wpkh-p2sh'): raise ScriptTypeNotSupported( _('This type of script is not supported with the Digital Bitbox.' )) devmgr = self.device_manager() client = devmgr.client_by_id(device_id) client.handler = self.create_handler(wizard) client.check_device_dialog() xpub = client.get_xpub(derivation, xtype) return xpub
def remove_local_tx(self, delete_tx): to_delete = {delete_tx} to_delete |= self.wallet.get_depending_transactions(delete_tx) question = _("Are you sure you want to remove this transaction?") if len(to_delete) > 1: question = _( "Are you sure you want to remove this transaction and {} child transactions?" .format(len(to_delete) - 1)) answer = QMessageBox.question(self.parent, _("Please confirm"), question, QMessageBox.Yes, QMessageBox.No) if answer == QMessageBox.No: return for tx in to_delete: self.wallet.remove_transaction(tx) self.wallet.save_transactions(write=True) # need to update at least: history_list, utxo_list, address_list self.parent.need_update.set()
def __init__(self, network, config, network_updated_signal_obj): QDialog.__init__(self) self.setWindowTitle(_('Network')) self.setMinimumSize(500, 20) self.nlayout = NetworkChoiceLayout(network, config) self.network_updated_signal_obj = network_updated_signal_obj vbox = QVBoxLayout(self) vbox.addLayout(self.nlayout.layout()) vbox.addLayout(Buttons(CloseButton(self))) self.network_updated_signal_obj.network_updated_signal.connect( self.on_update) network.register_callback(self.on_network, ['updated', 'interfaces'])
def sign_tx(self, wallet, tx): if not isinstance(wallet, self.wallet_class): return if not wallet.can_sign_without_server(): self.print_error("twofactor:sign_tx") auth_code = None if wallet.keystores['x3/'].get_tx_derivations(tx): msg = _('Please enter your Google Authenticator code:') auth_code = int(input(msg)) else: self.print_error("twofactor: xpub3 not needed") wallet.auth_code = auth_code
def do_auth(self, wizard, short_id, otp, xpub3): try: server.auth(short_id, otp) except: wizard.show_message(_('Incorrect password')) return k3 = keystore.from_xpub(xpub3) wizard.storage.put('x3/', k3.dump()) wizard.storage.put('use_trustedcoin', True) wizard.storage.write() wizard.wallet = Wallet_2fa(wizard.storage) wizard.run('create_addresses')
def auth_dialog(self, window): d = WindowModalDialog(window, _("Authorization")) vbox = QVBoxLayout(d) pw = AmountEdit(None, is_int=True) msg = _('Please enter your Google Authenticator code') vbox.addWidget(QLabel(msg)) grid = QGridLayout() grid.setSpacing(8) grid.addWidget(QLabel(_('Code')), 1, 0) grid.addWidget(pw, 1, 1) vbox.addLayout(grid) msg = _( 'If you have lost your second factor, you need to restore your wallet from seed in order to request a new code.' ) label = QLabel(msg) label.setWordWrap(1) vbox.addWidget(label) vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) if not d.exec_(): return return pw.get_amount()
def recover_or_erase_dialog(self): msg = _("The Digital Bitbox is already seeded. Choose an option:\n") choices = [ (_("Create a wallet using the current seed")), (_("Load a wallet from the micro SD card (the current seed is overwritten)" )), (_("Erase the Digital Bitbox")) ] try: reply = self.handler.win.query_choice(msg, choices) except Exception: return # Back button pushed if reply == 2: self.dbb_erase() elif reply == 1: if not self.dbb_load_backup(): return else: if self.hid_send_encrypt(b'{"device":"info"}')['device']['lock']: raise Exception("Full 2FA enabled. This is not supported yet.") # Use existing seed self.isInitialized = True
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, args=(settings, method, device_id, wizard, handler)) t.setDaemon(True) t.start() wizard.loop.exec_() wizard.choice_dialog(title=_('Initialize Device'), message=msg, choices=choices, run_next=f)
def do_verify(self, d): tx = d.tx wallet = d.wallet window = d.main_window if wallet.is_watching_only(): d.show_critical( _('This feature is not available for watch-only wallets.')) return # 1. get the password and sign the verification request password = None if wallet.has_keystore_encryption(): msg = _('GreenAddress requires your signature \n' 'to verify that transaction is instant.\n' 'Please enter your password to sign a\n' 'verification request.') password = window.password_dialog(msg, parent=d) if not password: return try: d.verify_button.setText(_('Verifying...')) QApplication.processEvents() # update the button label addr = self.get_my_addr(d) message = "Please verify if %s is GreenAddress instant confirmed" % tx.txid( ) sig = wallet.sign_message(addr, message, password) sig = base64.b64encode(sig).decode('ascii') # 2. send the request response = requests.request( "GET", ("https://greenaddress.it/verify/?signature=%s&txhash=%s" % (urllib.parse.quote(sig), tx.txid())), headers={'User-Agent': 'Electrum'}) response = response.json() # 3. display the result if response.get('verified'): d.show_message( _('{} is covered by GreenAddress instant confirmation' ).format(tx.txid()), title=_('Verification successful!')) else: d.show_critical( _('{} is not covered by GreenAddress instant confirmation' ).format(tx.txid()), title=_('Verification failed!')) except BaseException as e: import traceback traceback.print_exc(file=sys.stdout) d.show_error(str(e)) finally: d.verify_button.setText(self.button_label)
def receive_menu(self, menu, addrs, wallet): if len(addrs) != 1: return for keystore in wallet.get_keystores(): if type(keystore) == self.keystore_class: def show_address(): keystore.thread.add( partial(self.show_address, wallet, keystore, addrs[0])) menu.addAction( _("Show on {}").format(self.device), show_address) break
def create_menu(self, position): item = self.itemAt(position) if not item: return addr = str(item.text(1)) req = self.wallet.receive_requests[addr] column = self.currentColumn() column_title = self.headerItem().text(column) column_data = item.text(column) menu = QMenu(self) menu.addAction( _("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) menu.addAction( _("Copy URI"), lambda: self.parent.view_and_paste( 'URI', '', self.parent.get_request_URI(addr))) menu.addAction(_("Save as BIP70 file"), lambda: self.parent.export_payment_request(addr)) menu.addAction(_("Delete"), lambda: self.parent.delete_payment_request(addr)) run_hook('receive_list_menu', menu, addr) menu.exec_(self.viewport().mapToGlobal(position))
def seed_options(self): dialog = QDialog() vbox = QVBoxLayout(dialog) if 'ext' in self.options: cb_ext = QCheckBox(_('Extend this seed with custom words')) cb_ext.setChecked(self.is_ext) vbox.addWidget(cb_ext) if 'bip39' in self.options: def f(b): self.is_seed = (lambda x: bool(x)) if b else self.saved_is_seed self.is_bip39 = b self.on_edit() if b: msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.' ), _('However, we do not generate BIP39 seeds, because they do not meet our safety standard.' ), _('BIP39 seeds do not include a version number, which compromises compatibility with future software.' ), _('We do not guarantee that BIP39 imports will always be supported in Electrum.' ), ]) else: msg = '' self.seed_warning.setText(msg) cb_bip39 = QCheckBox(_('BIP39 seed')) cb_bip39.toggled.connect(f) cb_bip39.setChecked(self.is_bip39) vbox.addWidget(cb_bip39) vbox.addLayout(Buttons(OkButton(dialog))) if not dialog.exec_(): return None self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False self.is_bip39 = cb_bip39.isChecked( ) if 'bip39' in self.options else False
def message_dialog(self, msg, on_cancel): # Called more than once during signing, to confirm output and fee self.clear_dialog() title = _('Please check your {} device').format(self.device) self.dialog = dialog = WindowModalDialog(self.top_level_window(), title) l = QLabel(msg) vbox = QVBoxLayout(dialog) vbox.addWidget(l) if on_cancel: dialog.rejected.connect(on_cancel) vbox.addLayout(Buttons(CancelButton(dialog))) dialog.show()
def load_wallet(self, wallet, window): for keystore in wallet.get_keystores(): if not isinstance(keystore, self.keystore_class): continue if not self.libraries_available: window.show_error( _("Cannot find python library for") + " '%s'.\n" % self.name \ + _("Make sure you install it with python3") ) return tooltip = self.device + '\n' + (keystore.label or 'unnamed') cb = partial(self.show_settings_dialog, window, keystore) button = StatusBarButton(QIcon(self.icon_unpaired), tooltip, cb) button.icon_paired = self.icon_paired button.icon_unpaired = self.icon_unpaired window.statusBar().addPermanentWidget(button) handler = self.create_handler(window) handler.button = button keystore.handler = handler keystore.thread = TaskThread(window, window.on_error) # Trigger a pairing keystore.thread.add(partial(self.get_client, keystore))
def pin_dialog(self, msg): # Needed e.g. when resetting a device self.clear_dialog() dialog = WindowModalDialog(self.top_level_window(), _("Enter PIN")) matrix = self.pin_matrix_widget_class() vbox = QVBoxLayout() vbox.addWidget(QLabel(msg)) vbox.addWidget(matrix) vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog))) dialog.setLayout(vbox) dialog.exec_() self.response = str(matrix.get_value()) self.done.set()
def settings_dialog(self, window): d = WindowModalDialog(window, _("Email settings")) d.setMinimumSize(500, 200) vbox = QVBoxLayout(d) vbox.addWidget(QLabel(_('Server hosting your email acount'))) 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) username = str(username_e.text()) self.config.set_key('email_username', username) password = str(password_e.text()) self.config.set_key('email_password', password)
def restore_choice(self, wizard, seed, passphrase): wizard.set_icon(':icons/trustedcoin-wizard.png') wizard.stack = [] title = _('Restore 2FA wallet') msg = ' '.join([ 'You are going to restore a wallet protected with two-factor authentication.', 'Do you want to keep using two-factor authentication with this wallet,', 'or do you want to disable it, and have two master private keys in your wallet?' ]) choices = [('keep', 'Keep'), ('disable', 'Disable')] f = lambda x: self.on_choice(wizard, seed, passphrase, x) wizard.choice_dialog(choices=choices, message=msg, title=title, run_next=f)
def dbb_load_backup(self, show_msg=True): backups = self.hid_send_encrypt(b'{"backup":"list"}') if 'error' in backups: raise Exception(backups['error']['message']) try: f = self.handler.win.query_choice(_("Choose a backup file:"), backups['backup']) except Exception: return False # Back button pushed key = self.backup_password_dialog() if key is None: raise Exception('Canceled by user') key = self.stretch_key(key) if show_msg: self.handler.show_message(_("Loading backup...\r\n\r\n" \ "To continue, touch the Digital Bitbox's light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the light or wait for the timeout.")) msg = b'{"seed":{"source": "backup", "key": "%s", "filename": "%s"}}' % ( key, backups['backup'][f].encode('utf8')) hid_reply = self.hid_send_encrypt(msg) self.handler.finished() if 'error' in hid_reply: raise Exception(hid_reply['error']['message']) return True
def pw_changed(self): password = self.new_pw.text() if password: colors = { "Weak": "Red", "Medium": "Blue", "Strong": "Green", "Very Strong": "Green" } strength = check_password_strength(password) label = (_("Password Strength") + ": " + "<font color=" + colors[strength] + ">" + strength + "</font>") else: label = "" self.pw_strength.setText(label)
def multisig_dialog(self, run_next): cw = CosignWidget(2, 2) m_edit = QSlider(Qt.Horizontal, self) n_edit = QSlider(Qt.Horizontal, self) n_edit.setMinimum(2) n_edit.setMaximum(15) m_edit.setMinimum(1) m_edit.setMaximum(2) n_edit.setValue(2) m_edit.setValue(2) n_label = QLabel() m_label = QLabel() grid = QGridLayout() grid.addWidget(n_label, 0, 0) grid.addWidget(n_edit, 0, 1) grid.addWidget(m_label, 1, 0) grid.addWidget(m_edit, 1, 1) def on_m(m): m_label.setText(_('Require {0} signatures').format(m)) cw.set_m(m) def on_n(n): n_label.setText(_('From {0} cosigners').format(n)) cw.set_n(n) m_edit.setMaximum(n) n_edit.valueChanged.connect(on_n) m_edit.valueChanged.connect(on_m) on_n(2) on_m(2) vbox = QVBoxLayout() vbox.addWidget(cw) vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:"))) vbox.addLayout(grid) self.exec_layout(vbox, _("Multi-Signature Wallet")) m = int(m_edit.value()) n = int(n_edit.value()) return (m, n)
def on_edit(self): from electrum_bca.bitcoin import seed_type 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_bca.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) self.parent.next_button.setEnabled(b)
def seed_warning_msg(seed): return ''.join([ "<p>", _("Please save these {0} words on paper (order is important). "), _("This seed will allow you to recover your wallet in case " "of computer failure."), "</p>", "<b>" + _("WARNING") + ":</b>", "<ul>", "<li>" + _("Never disclose your seed.") + "</li>", "<li>" + _("Never type it on a website.") + "</li>", "<li>" + _("Do not store it electronically.") + "</li>", "</ul>" ]).format(len(seed.split()))
def __init__(self, parent=None): MyTreeWidget.__init__(self, parent, self.create_menu, [], 1) self.refresh_headers() self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.show_change = 0 self.show_used = 0 self.change_button = QComboBox(self) self.change_button.currentIndexChanged.connect(self.toggle_change) for t in [_('Receiving'), _('Change'), _('All')]: self.change_button.addItem(t) self.used_button = QComboBox(self) self.used_button.currentIndexChanged.connect(self.toggle_used) for t in [_('All'), _('Unused'), _('Funded'), _('Used')]: self.used_button.addItem(t)
def show_address(self, wallet, address): client = self.get_client(wallet.keystore) if not client.atleast_version(1, 3): wallet.keystore.handler.show_error( _("Your device firmware is too old")) return change, index = wallet.get_address_index(address) derivation = wallet.keystore.derivation address_path = "%s/%d/%d" % (derivation, change, index) address_n = client.expand_path(address_path) segwit = wallet.keystore.is_segwit() script_type = self.types.SPENDP2SHWITNESS if segwit else self.types.SPENDADDRESS client.get_address(self.get_coin_name(), address_n, True, script_type=script_type)
def change_homescreen(): from PIL import Image # FIXME dialog = QFileDialog(self, _("Choose Homescreen")) filename, __ = dialog.getOpenFileName() if filename: im = Image.open(str(filename)) if im.size != (hs_cols, hs_rows): raise Exception('Image must be 64 x 128 pixels') im = im.convert('1') pix = im.load() img = '' for j in range(hs_rows): for i in range(hs_cols): img += '1' if pix[i, j] else '0' img = ''.join( chr(int(img[i:i + 8], 2)) for i in range(0, len(img), 8)) invoke_client('change_homescreen', img)
def item_changed(self, item): if item is None: return if not item.isSelected(): return addr = str(item.text(1)) req = self.wallet.receive_requests[addr] expires = age(req['time'] + req['exp']) if req.get('exp') else _('Never') amount = req['amount'] message = self.wallet.labels.get(addr, '') self.parent.receive_address_e.setText(addr) self.parent.receive_message_e.setText(message) self.parent.receive_amount_e.setAmount(amount) self.parent.expires_combo.hide() self.parent.expires_label.show() self.parent.expires_label.setText(expires) self.parent.new_request_button.setEnabled(True)
def on_update(self): inv_list = self.parent.invoices.unpaid_invoices() self.clear() for pr in inv_list: key = pr.get_id() status = self.parent.invoices.get_status(key) requestor = pr.get_requestor() exp = pr.get_expiration_date() date_str = format_time(exp) if exp else _('Never') item = QTreeWidgetItem([date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')]) item.setIcon(4, QIcon(pr_icons.get(status))) item.setData(0, Qt.UserRole, key) item.setFont(1, QFont(MONOSPACE_FONT)) item.setFont(3, QFont(MONOSPACE_FONT)) self.addTopLevelItem(item) self.setCurrentItem(self.topLevelItem(0)) self.setVisible(len(inv_list)) self.parent.invoices_label.setVisible(len(inv_list))
def __init__(self, config, app, plugins, storage): BaseWizard.__init__(self, config, storage) QDialog.__init__(self, None) self.setWindowTitle('Electrum - ' + _('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.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/electrum-bca.png') self.show() self.raise_() self.refresh_gui() # Need for QT on MacOSX. Lame.