def qr_input(self): from electrum_dash import qrscanner, get_config try: data = qrscanner.scan_qr(get_config()) except BaseException, e: QMessageBox.warning(self, _('Error'), _(e), _('OK')) return ""
def __init__(self, parent): super(CharacterDialog, self).__init__(parent) self.setWindowTitle(_("KeepKey Seed Recovery")) self.character_pos = 0 self.word_pos = 0 self.loop = QEventLoop() self.word_help = QLabel() self.char_buttons = [] vbox = QVBoxLayout(self) vbox.addWidget(WWLabel(CHARACTER_RECOVERY)) hbox = QHBoxLayout() hbox.addWidget(self.word_help) for i in range(4): char_button = CharacterButton('*') char_button.setMaximumWidth(36) self.char_buttons.append(char_button) hbox.addWidget(char_button) self.accept_button = CharacterButton(_("Accept Word")) self.accept_button.clicked.connect(partial(self.process_key, 32)) self.rejected.connect(partial(self.loop.exit, 1)) hbox.addWidget(self.accept_button) hbox.addStretch(1) vbox.addLayout(hbox) self.finished_button = QPushButton(_("Seed Entered")) self.cancel_button = QPushButton(_("Cancel")) self.finished_button.clicked.connect(partial(self.process_key, Qt.Key_Return)) self.cancel_button.clicked.connect(self.rejected) buttons = Buttons(self.finished_button, self.cancel_button) vbox.addSpacing(40) vbox.addLayout(buttons) self.refresh() self.show()
def on_vote_successful(result): errmsg, res = result if res: QMessageBox.information(self, _('Success'), _('Successfully voted')) else: QMessageBox.critical(self, _('Error Voting'), _(errmsg)) self.proposals_widget.editor.vote_button.setEnabled(True)
def build_tray_menu(self): m = QMenu() m.addAction(_("Show/Hide"), self.show_or_hide) m.addAction(_("Dark/Light"), self.toggle_tray_icon) m.addSeparator() m.addAction(_("Exit Electrum-DASH"), self.close) self.tray.setContextMenu(m)
def __init__(self, transaction_id, parent): super(TransactionWindow, self).__init__() self.tx_id = str(transaction_id) self.parent = parent self.setModal(True) self.resize(200, 100) self.setWindowTitle(_("Transaction successfully sent")) self.layout = QGridLayout(self) history_label = "%s\n%s" % ( _("Your transaction has been sent."), _("Please enter a label for this transaction for future reference."), ) self.layout.addWidget(QLabel(history_label)) self.label_edit = QLineEdit() self.label_edit.setPlaceholderText(_("Transaction label")) self.label_edit.setObjectName("label_input") self.label_edit.setAttribute(Qt.WA_MacShowFocusRect, 0) self.label_edit.setFocusPolicy(Qt.ClickFocus) self.layout.addWidget(self.label_edit) self.save_button = QPushButton(_("Save")) self.layout.addWidget(self.save_button) self.save_button.clicked.connect(self.set_label) self.exec_()
def cast_vote(self, proposal_name, vote_yes): """Vote for a proposal. This is called by ProposalsWidget.""" vote_choice = 'yes' if vote_yes else 'no' mn = self.selected_masternode() if not mn.announced: return QMessageBox.critical(self, _('Cannot Vote'), _('Masternode has not been activated.')) # Check that we can vote before asking for a password. try: self.manager.check_can_vote(mn.alias, proposal_name) except Exception as e: return QMessageBox.critical(self, _('Cannot Vote'), _(str(e))) self.proposals_widget.editor.vote_button.setEnabled(False) def vote_thread(): return self.manager.vote(mn.alias, proposal_name, vote_choice) # Show the result. def on_vote_successful(result): errmsg, res = result if res: QMessageBox.information(self, _('Success'), _('Successfully voted')) else: QMessageBox.critical(self, _('Error Voting'), _(errmsg)) self.proposals_widget.editor.vote_button.setEnabled(True) def on_vote_failed(err): self.print_error('Error sending vote:') # Print traceback information to error log. self.print_error(''.join(traceback.format_tb(err[2]))) self.print_error(''.join(traceback.format_exception_only(err[0], err[1]))) self.proposals_widget.editor.vote_button.setEnabled(True) util.WaitingDialog(self, _('Voting...'), vote_thread, on_vote_successful, on_vote_failed)
def delete_current_masternode(self): """Delete the masternode that is being viewed.""" mn = self.selected_masternode() if QMessageBox.question(self, _('Delete'), _('Do you want to remove the masternode configuration for') + ' %s?'%mn.alias, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes: self.masternodes_widget.remove_masternode(mn.alias) self.masternodes_widget.view.selectRow(0)
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 Dash-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 Dash-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 __init__(self, parent): MyTreeWidget.__init__(self, parent, self.create_menu, [_('Date'), _('Address'), '', _('Description'), _('Amount'), _('Status')], 3) self.currentItemChanged.connect(self.item_changed) self.itemClicked.connect(self.item_changed) self.setSortingEnabled(True) self.setColumnWidth(0, 180) self.hideColumn(1)
def make_cypherseed(self, img, rawnoise, calibration=False, is_seed = True): img = img.convertToFormat(QImage.Format_Mono) p = QPainter() p.begin(img) p.setCompositionMode(26) #xor p.drawImage(0, 0, rawnoise) p.end() cypherseed = self.pixelcode_2x2(img) cypherseed = QBitmap.fromImage(cypherseed) cypherseed = cypherseed.scaled(self.f_size, Qt.KeepAspectRatio) cypherseed = self.overlay_marks(cypherseed, True, calibration) if not is_seed: self.filename = _('custom_secret')+'_' self.was = _('Custom secret') else: self.filename = self.wallet_name+'_'+ _('seed')+'_' self.was = self.wallet_name +' ' + _('seed') if self.has_extension: self.ext_warning(self.c_dialog) if not calibration: self.toPdf(QImage(cypherseed)) QDesktopServices.openUrl (QUrl.fromLocalFile(os.path.abspath(self.base_dir+self.filename+self.version+'_'+self.code_id+'.pdf'))) cypherseed.save(self.base_dir + self.filename +self.version + '_'+ self.code_id + '.png') self.bcrypt(self.c_dialog) return cypherseed
def get_library_not_available_message(self) -> str: if hasattr(self, 'libraries_available_message'): message = self.libraries_available_message else: message = _("Missing libraries for {}.").format(self.name) message += '\n' + _("Make sure you install it with python3") return message
def multi_mpk_dialog(self, xpub_hot, n): vbox = QVBoxLayout() scroll = QScrollArea() scroll.setEnabled(True) scroll.setWidgetResizable(True) vbox.addWidget(scroll) w = QWidget() scroll.setWidget(w) innerVbox = QVBoxLayout() w.setLayout(innerVbox) vbox0 = seed_dialog.show_seed_box(MSG_SHOW_MPK, xpub_hot, 'hot') innerVbox.addLayout(vbox0) entries = [] for i in range(n): msg = _("Please enter the master public key of cosigner") + ' %d'%(i+1) vbox2, seed_e2 = seed_dialog.enter_seed_box(msg, self, 'cold') innerVbox.addLayout(vbox2) entries.append(seed_e2) vbox.addStretch(1) button = OkButton(self, _('Next')) vbox.addLayout(Buttons(CancelButton(self), button)) button.setEnabled(False) f = lambda: button.setEnabled( map(lambda e: Wallet.is_xpub(self.get_seed_text(e)), entries) == [True]*len(entries)) for e in entries: e.textChanged.connect(f) self.set_layout(vbox) if not self.exec_(): return return map(lambda e: self.get_seed_text(e), entries)
def get_tooltip(self, pos, fee_rate): mempool = self.config.use_mempool_fees() target, estimate = self.config.get_fee_text(pos, self.dyn, mempool, fee_rate) if self.dyn: return _('Target') + ': ' + target + '\n' + _('Current rate') + ': ' + estimate else: return _('Fixed rate') + ': ' + target + '\n' + _('Estimate') + ': ' + estimate
def toggle_passphrase(self): if self.features.passphrase_protection: self.msg = _("Confirm on your {} device to disable passphrases") else: self.msg = _("Confirm on your {} device to enable passphrases") enabled = not self.features.passphrase_protection self.apply_settings(use_passphrase=enabled)
def save_current_masternode(self, as_new=False): """Save the masternode that is being viewed. If as_new is True, a new masternode will be created. """ delegate_privkey = str(self.masternode_editor.delegate_key_edit.text()) if not delegate_privkey: QMessageBox.warning(self, _('Warning'), _('Delegate private key is empty.')) return try: delegate_pubkey = self.manager.import_masternode_delegate(delegate_privkey) except Exception: # Show an error if the private key is invalid and not an empty string. if delegate_privkey: QMessageBox.warning(self, _('Warning'), _('Ignoring invalid delegate private key.')) delegate_pubkey = '' alias = str(self.masternode_editor.alias_edit.text()) # Construct a new masternode. if as_new: kwargs = self.masternode_editor.get_masternode_args() kwargs['delegate_key'] = delegate_pubkey del kwargs['vin'] self.mapper.revert() self.masternodes_widget.add_masternode(MasternodeAnnounce(**kwargs)) else: self.mapper.submit() self.manager.save() self.masternodes_widget.select_masternode(alias)
def mouseReleaseEvent(self, event): dialog = QDialog(self) dialog.setWindowTitle(_('Electrum update')) dialog.setModal(1) main_layout = QGridLayout() main_layout.addWidget(QLabel(_("A new version of Electrum is available:")+" " + self.latest_version), 0,0,1,3) ignore_version = QPushButton(_("Ignore this version")) ignore_version.clicked.connect(self.ignore_this_version) ignore_all_versions = QPushButton(_("Ignore all versions")) ignore_all_versions.clicked.connect(self.ignore_all_version) open_website = QPushButton(_("Goto download page")) open_website.clicked.connect(self.open_website) main_layout.addWidget(ignore_version, 1, 0) main_layout.addWidget(ignore_all_versions, 1, 1) main_layout.addWidget(open_website, 1, 2) dialog.setLayout(main_layout) self.dialog = dialog if not dialog.exec_(): return
def sign_announce(self, alias): """Sign an announce for alias. This is called by SignAnnounceWidget.""" pw = None if self.manager.wallet.has_password(): pw = self.gui.password_dialog(msg=_('Please enter your password to activate masternode "%s".' % alias)) if pw is None: return self.sign_announce_widget.sign_button.setEnabled(False) def sign_thread(): return self.manager.sign_announce(alias, pw) def on_sign_successful(mn): self.print_msg('Successfully signed Masternode Announce.') self.send_announce(alias) # Proceed to broadcasting the announcement, or re-enable the button. def on_sign_error(err): self.print_error('Error signing MasternodeAnnounce:') # Print traceback information to error log. self.print_error(''.join(traceback.format_tb(err[2]))) self.print_error(''.join(traceback.format_exception_only(err[0], err[1]))) self.sign_announce_widget.sign_button.setEnabled(True) util.WaitingDialog(self, _('Signing Masternode Announce...'), sign_thread, on_sign_successful, on_sign_error)
def __init__(self, address, parent): self.address = address self.parent = parent self.config = parent.config self.wallet = parent.wallet self.app = parent.app self.saved = True QDialog.__init__(self) self.setMinimumWidth(700) self.setWindowTitle(_("Address")) self.setModal(1) vbox = QVBoxLayout() self.setLayout(vbox) vbox.addWidget(QLabel(_("Address:"))) self.addr_e = ButtonsLineEdit(self.address) self.addr_e.addCopyButton(self.app) self.addr_e.addButton(":icons/qrcode.png", self.show_qr, _("Show QR Code")) self.addr_e.setReadOnly(True) vbox.addWidget(self.addr_e) vbox.addWidget(QLabel(_("History"))) self.hw = HistoryWidget(self.parent) vbox.addWidget(self.hw) vbox.addStretch(1) vbox.addLayout(Buttons(CloseButton(self))) self.format_amount = self.parent.format_amount h = self.wallet.get_history([self.address]) self.hw.update(h)
def __init__(self, parent): super(MatrixDialog, self).__init__(parent) self.setWindowTitle(_("Trezor Matrix Recovery")) self.num = 9 self.loop = QEventLoop() vbox = QVBoxLayout(self) vbox.addWidget(WWLabel(MATRIX_RECOVERY)) grid = QGridLayout() grid.setSpacing(0) self.char_buttons = [] for y in range(3): for x in range(3): button = QPushButton('?') button.clicked.connect(partial(self.process_key, ord('1') + y * 3 + x)) grid.addWidget(button, 3 - y, x) self.char_buttons.append(button) vbox.addLayout(grid) self.backspace_button = QPushButton("<=") self.backspace_button.clicked.connect(partial(self.process_key, Qt.Key_Backspace)) self.cancel_button = QPushButton(_("Cancel")) self.cancel_button.clicked.connect(partial(self.process_key, Qt.Key_Escape)) buttons = Buttons(self.backspace_button, self.cancel_button) vbox.addSpacing(40) vbox.addLayout(buttons) self.refresh() self.show()
def on_change_hist(checked): if checked: self.config.set_key('history_rates', 'checked') self.request_history_rates() else: self.config.set_key('history_rates', 'unchecked') self.win.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] ) self.win.history_list.setColumnCount(5)
def __init__(self, text="", allow_multi=False): ButtonsTextEdit.__init__(self, text) self.allow_multi = allow_multi self.setReadOnly(0) self.addButton(":icons/file.png", self.file_input, _("Read file")) icon = ":icons/qrcode.png" self.addButton(icon, self.qr_input, _("Read QR code")) run_hook('scan_text_edit', self)
def export(self): name = 'signed_%s.txn' % (self.tx.txid()[0:8]) if self.tx.is_complete() else 'unsigned.txn' fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn") if fileName: with open(fileName, "w+") as f: f.write(json.dumps(self.tx.as_dict(), indent=4) + '\n') self.show_message(_("Transaction exported successfully")) self.saved = True
def set_pin(self, remove): if remove: self.msg = _("Confirm on your {} device to disable PIN protection") elif self.features.pin_protection: self.msg = _("Confirm on your {} device to change your PIN") else: self.msg = _("Confirm on your {} device to set a PIN") self.change_pin(remove)
def history_tab_update(self): # Disabled. return if self.config.get('history_rates') != "checked": return if not self.resp_hist: return if not self.wallet: return self.win.is_edit = True self.win.history_list.setColumnCount(6) self.win.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance'), _('Fiat Amount')] ) root = self.win.history_list.invisibleRootItem() childcount = root.childCount() for i in range(childcount): item = root.child(i) try: tx_info = self.tx_list[str(item.data(0, Qt.UserRole).toPyObject())] except Exception: newtx = self.wallet.get_history() v = newtx[[x[0] for x in newtx].index(str(item.data(0, Qt.UserRole).toPyObject()))][2] tx_info = {'timestamp':int(time.time()), 'value': v} pass tx_time = int(tx_info['timestamp']) tx_value = Decimal(str(tx_info['value'])) / COIN if self.cur_exchange == "CoinDesk": tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d') try: tx_fiat_val = "%.2f %s" % (tx_value * Decimal(self.resp_hist['bpi'][tx_time_str]), "USD") except KeyError: tx_fiat_val = "%.2f %s" % (self.btc_rate * Decimal(str(tx_info['value']))/COIN , "USD") elif self.cur_exchange == "Winkdex": tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d') + "T16:00:00-04:00" try: tx_rate = self.resp_hist[[x['timestamp'] for x in self.resp_hist].index(tx_time_str)]['price'] tx_fiat_val = "%.2f %s" % (tx_value * Decimal(tx_rate)/Decimal("100.0"), "USD") except ValueError: tx_fiat_val = "%.2f %s" % (self.btc_rate * Decimal(tx_info['value'])/COIN , "USD") except KeyError: tx_fiat_val = _("No data") elif self.cur_exchange == "BitcoinVenezuela": tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d') try: num = self.resp_hist[tx_time_str].replace(',','') tx_fiat_val = "%.2f %s" % (tx_value * Decimal(num), self.fiat_unit()) except KeyError: tx_fiat_val = _("No data") tx_fiat_val = " "*(12-len(tx_fiat_val)) + tx_fiat_val item.setText(5, tx_fiat_val) item.setFont(5, QFont(MONOSPACE_FONT)) if Decimal(str(tx_info['value'])) < 0: item.setForeground(5, QBrush(QColor("#BC1E1E"))) self.win.history_list.setColumnWidth(5, 120) self.win.is_edit = False
def verify_seed(self, seed, sid, func=None): r = self.enter_seed_dialog(MSG_VERIFY_SEED, sid, func) if not r: return if prepare_seed(r) != prepare_seed(seed): QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK')) return False else: return True
def add_io(self, vbox): if self.tx.locktime > 0: vbox.addWidget(QLabel("LockTime: %d\n" % self.tx.locktime)) vbox.addWidget(QLabel(_("Inputs"))) ext = QTextCharFormat() rec = QTextCharFormat() rec.setBackground(QBrush(QColor("lightgreen"))) rec.setToolTip(_("Wallet receive address")) chg = QTextCharFormat() chg.setBackground(QBrush(QColor("yellow"))) chg.setToolTip(_("Wallet change address")) def text_format(addr): if self.wallet.is_mine(addr): return chg if self.wallet.is_change(addr) else rec return ext i_text = QTextEdit() i_text.setFont(QFont(MONOSPACE_FONT)) i_text.setReadOnly(True) i_text.setMaximumHeight(100) cursor = i_text.textCursor() for x in self.tx.inputs: if x.get('is_coinbase'): cursor.insertText('coinbase') else: prevout_hash = x.get('prevout_hash') prevout_n = x.get('prevout_n') cursor.insertText(prevout_hash[0:8] + '...', ext) cursor.insertText(prevout_hash[-8:] + ":%-4d " % prevout_n, ext) addr = x.get('address') if addr == "(pubkey)": _addr = self.wallet.find_pay_to_pubkey_address(prevout_hash, prevout_n) if _addr: addr = _addr if addr is None: addr = _('unknown') cursor.insertText(addr, text_format(addr)) cursor.insertBlock() vbox.addWidget(i_text) vbox.addWidget(QLabel(_("Outputs"))) o_text = QTextEdit() o_text.setFont(QFont(MONOSPACE_FONT)) o_text.setReadOnly(True) o_text.setMaximumHeight(100) cursor = o_text.textCursor() for addr, v in self.tx.get_outputs(): cursor.insertText(addr, text_format(addr)) if v is not None: cursor.insertText('\t', ext) cursor.insertText(self.parent.format_amount(v, whitespaces = True), ext) cursor.insertBlock() vbox.addWidget(o_text)
def setup_device(self, device_info, wizard, purpose): devmgr = self.device_manager() device_id = device_info.device.id_ client = devmgr.client_by_id(device_id) if client is None: raise Exception(_('Failed to create a client for this device.') + '\n' + _('Make sure it is in the correct state.')) client.handler = self.create_handler(wizard) client.get_xpub("m/44'/5'", 'standard') # TODO replace by direct derivation once Nano S > 1.1
def __init__(self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None, for_seed_words=True): QVBoxLayout.__init__(self) self.parent = parent self.options = options if title: self.addWidget(WWLabel(title)) if seed: # "read only", we already have the text if for_seed_words: self.seed_e = ButtonsTextEdit() else: # e.g. xpub self.seed_e = ShowQRTextEdit() self.seed_e.setReadOnly(True) self.seed_e.setText(seed) else: # we expect user to enter text assert for_seed_words self.seed_e = CompletionTextEdit() self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) self.initialize_completer() self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() if icon: logo = QLabel() logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) hbox.addWidget(self.seed_e) self.addLayout(hbox) hbox = QHBoxLayout() hbox.addStretch(1) self.seed_type_label = QLabel('') hbox.addWidget(self.seed_type_label) # options self.is_bip39 = False self.is_ext = False if options: opt_button = EnterButton(_('Options'), self.seed_options) hbox.addWidget(opt_button) self.addLayout(hbox) if passphrase: hbox = QHBoxLayout() passphrase_e = QLineEdit() passphrase_e.setText(passphrase) passphrase_e.setReadOnly(True) hbox.addWidget(QLabel(_("Your seed extension is") + ':')) hbox.addWidget(passphrase_e) self.addLayout(hbox) self.addStretch(1) self.seed_warning = WWLabel('') if msg: self.seed_warning.setText(seed_warning_msg(seed)) self.addWidget(self.seed_warning)
def on_update(self): self.wallet = self.parent.wallet item = self.currentItem() current_address = item.data(0, Qt.UserRole) if item else None if self.show_change == 1: addr_list = self.wallet.get_receiving_addresses() elif self.show_change == 2: addr_list = self.wallet.get_change_addresses() else: addr_list = self.wallet.get_addresses() self.clear() fx = self.parent.fx for address in addr_list: num = self.wallet.get_address_history_len(address) label = self.wallet.labels.get(address, '') c, u, x = self.wallet.get_addr_balance(address) balance = c + u + x is_used_and_empty = self.wallet.is_used(address) and balance == 0 if self.show_used == 1 and (balance or is_used_and_empty): continue if self.show_used == 2 and balance == 0: continue if self.show_used == 3 and not is_used_and_empty: continue balance_text = self.parent.format_amount(balance, whitespaces=True) # create item if fx and fx.get_fiat_address_config(): rate = fx.exchange_rate() fiat_balance = fx.value_str(balance, rate) address_item = SortableTreeWidgetItem(['', address, label, balance_text, fiat_balance, "%d"%num]) else: address_item = SortableTreeWidgetItem(['', address, label, balance_text, "%d"%num]) # align text and set fonts for i in range(address_item.columnCount()): address_item.setTextAlignment(i, Qt.AlignVCenter) if i not in (0, 2): address_item.setFont(i, QFont(MONOSPACE_FONT)) if fx and fx.get_fiat_address_config(): address_item.setTextAlignment(4, Qt.AlignRight | Qt.AlignVCenter) # setup column 0 if self.wallet.is_change(address): address_item.setText(0, _('change')) address_item.setBackground(0, ColorScheme.YELLOW.as_color(True)) else: address_item.setText(0, _('receiving')) address_item.setBackground(0, ColorScheme.GREEN.as_color(True)) address_item.setData(0, Qt.UserRole, address) # column 0; independent from address column # setup column 1 if self.wallet.is_frozen(address): address_item.setBackground(1, ColorScheme.BLUE.as_color(True)) if self.wallet.is_beyond_limit(address): address_item.setBackground(1, ColorScheme.RED.as_color(True)) # add item self.addChild(address_item) if address == current_address: self.setCurrentItem(address_item)
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 on_filename(filename): # FIXME? "filename" might contain ".." (etc) and hence sketchy path traversals are possible nonlocal temp_storage temp_storage = None msg = None if filename: path = os.path.join(wallet_folder, filename) wallet_from_memory = get_wallet_from_daemon(path) try: if wallet_from_memory: temp_storage = wallet_from_memory.storage # type: Optional[WalletStorage] else: temp_storage = WalletStorage(path) except (StorageReadWriteError, WalletFileException) as e: msg = _('Cannot read file') + f'\n{repr(e)}' except Exception as e: self.logger.exception('') msg = _('Cannot read file') + f'\n{repr(e)}' else: msg = "" self.next_button.setEnabled(temp_storage is not None) user_needs_to_enter_password = False if temp_storage: if not temp_storage.file_exists(): msg =_("This file does not exist.") + '\n' \ + _("Press 'Next' to create this wallet, or choose another file.") elif not wallet_from_memory: if temp_storage.is_encrypted_with_user_pw(): msg = _("This file is encrypted with a password.") + '\n' \ + _('Enter your password or choose another file.') user_needs_to_enter_password = True elif temp_storage.is_encrypted_with_hw_device(): msg = _("This file is encrypted using a hardware device.") + '\n' \ + _("Press 'Next' to choose device to decrypt.") else: msg = _("Press 'Next' to open this wallet.") else: msg = _("This file is already open in memory.") + "\n" \ + _("Press 'Next' to create/focus window.") if msg is None: msg = _('Cannot read file') msg_label.setText(msg) widget_create_new.setVisible( bool(temp_storage and temp_storage.file_exists())) if user_needs_to_enter_password: pw_label.show() pw_e.show() pw_e.setFocus() else: pw_label.hide() pw_e.hide()
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> QVariant: assert index.isValid() col = index.column() coin_item = index.internalPointer() address = coin_item['address'] is_frozen_addr = coin_item['is_frozen_addr'] is_frozen_coin = coin_item['is_frozen_coin'] height = coin_item['height'] time_str = '' if self.view.config.get('show_utxo_time', False): prevout_timestamp = coin_item['prevout_timestamp'] time_str = (format_time(prevout_timestamp) if prevout_timestamp < math.inf else _('unknown')) outpoint = coin_item['outpoint'] out_short = coin_item['out_short'] label = coin_item['label'] balance = coin_item['balance'] ps_rounds = coin_item['ps_rounds'] is_ps_ks = coin_item['is_ps_ks'] if ps_rounds is None: ps_rounds = 'N/A' else: ps_rounds = ps_coin_rounds_str(ps_rounds) if (role == self.view.ROLE_CLIPBOARD_DATA and col == UTXOColumns.OUTPOINT): return QVariant(outpoint) elif role == Qt.ToolTipRole: if col == UTXOColumns.ADDRESS and is_frozen_addr: return QVariant(_('Address is frozen')) elif col == UTXOColumns.OUTPOINT and is_frozen_coin: return QVariant(f'{outpoint}\n{_("Coin is frozen")}') elif outpoint in (self.view._spend_set or set()): if col == UTXOColumns.OUTPOINT: return QVariant(f'{outpoint}\n{SELECTED_TO_SPEND_TOOLTIP}') else: return QVariant(SELECTED_TO_SPEND_TOOLTIP) elif col == UTXOColumns.OUTPOINT: return QVariant(outpoint) elif role not in (Qt.DisplayRole, Qt.EditRole): if role == Qt.TextAlignmentRole: if col in [ UTXOColumns.DATE, UTXOColumns.AMOUNT, UTXOColumns.HEIGHT, UTXOColumns.PS_ROUNDS, UTXOColumns.KEYSTORE_TYPE ]: return QVariant(Qt.AlignRight | Qt.AlignVCenter) else: return QVariant(Qt.AlignVCenter) elif role == Qt.FontRole: return QVariant(QFont(MONOSPACE_FONT)) elif role == Qt.BackgroundRole: if col == UTXOColumns.ADDRESS and is_frozen_addr: return QVariant(ColorScheme.BLUE.as_color(True)) elif col == UTXOColumns.OUTPOINT and is_frozen_coin: return QVariant(ColorScheme.BLUE.as_color(True)) elif outpoint in (self.view._spend_set or set()): return QVariant(ColorScheme.GREEN.as_color(True)) elif col == UTXOColumns.DATE: return QVariant(time_str) elif col == UTXOColumns.OUTPOINT: return QVariant(out_short) elif col == UTXOColumns.ADDRESS: return QVariant(address) elif col == UTXOColumns.LABEL: return QVariant(label) elif col == UTXOColumns.AMOUNT: return QVariant(balance) elif col == UTXOColumns.HEIGHT: return QVariant(height) elif col == UTXOColumns.PS_ROUNDS: return QVariant(ps_rounds) elif col == UTXOColumns.KEYSTORE_TYPE: return QVariant(_('PS Keystore') if is_ps_ks else _('Main')) else: return QVariant()
from PyQt5.QtCore import (pyqtSignal, Qt, QModelIndex, QVariant, QAbstractItemModel, QItemSelectionModel) from PyQt5.QtGui import QFont from PyQt5.QtWidgets import (QAbstractItemView, QHeaderView, QComboBox, QLabel, QMenu) from electrum_dash.i18n import _ from electrum_dash.transaction import PartialTxInput from electrum_dash.dash_ps_util import (PSCoinRounds, sort_utxos_by_ps_rounds, ps_coin_rounds_str) from electrum_dash.logging import Logger from electrum_dash.util import profiler, format_time from .util import MyTreeView, ColorScheme, MONOSPACE_FONT, EnterButton, GetDataThread SELECTED_TO_SPEND_TOOLTIP = _('Coin selected to be spent') class PSStateFilter(IntEnum): ALL = 0 PS = 1 PS_OTHER = 2 REGULAR = 3 def ui_text(self) -> str: return { self.ALL: _('All'), self.PS: _('PrivateSend'), self.PS_OTHER: _('PS Other coins'), self.REGULAR: _('Regular'), }[self]
from electrum_dash import Wallet, WalletStorage from electrum_dash.util import UserCancelled, InvalidPassword from electrum_dash.base_wizard import BaseWizard from electrum_dash.i18n import _ from .seed_dialog import SeedLayout, KeysLayout from .network_dialog import NetworkChoiceLayout from .util import * from .password_dialog import PasswordLayout, PW_NEW class GoBack(Exception): pass MSG_GENERATING_WAIT = _( "Electrum-DASH is generating your addresses, please wait...") MSG_ENTER_ANYTHING = _("Please enter a seed phrase, a master key, a list of " "Dash addresses, or a list of private keys") MSG_ENTER_SEED_OR_MPK = _( "Please enter a seed phrase or a master key (xpub or xprv):") MSG_COSIGNER = _("Please enter the master public key of cosigner #%d:") MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\ + _("Leave this field empty if you want to disable encryption.") MSG_RESTORE_PASSPHRASE = \ _("Please enter your seed derivation passphrase. " "Note: this is NOT your encryption password. " "Leave this field empty if you did not use one or are unsure.") class CosignWidget(QWidget): size = 120
def on_n(n): n_label.setText(_('From {0} cosigners').format(n)) cw.set_n(n) m_edit.setMaximum(n) backup_warning_label.setVisible(cw.m != cw.n)
class RequestList(MyTreeView): class Columns(IntEnum): DATE = 0 DESCRIPTION = 1 AMOUNT = 2 STATUS = 3 headers = { Columns.DATE: _('Date'), Columns.DESCRIPTION: _('Description'), Columns.AMOUNT: _('Amount'), Columns.STATUS: _('Status'), } filter_columns = [Columns.DATE, Columns.DESCRIPTION, Columns.AMOUNT] def __init__(self, parent: 'ElectrumWindow'): super().__init__(parent, self.create_menu, stretch_column=self.Columns.DESCRIPTION, editable_columns=[]) self.wallet = self.parent.wallet self.std_model = QStandardItemModel(self) self.proxy = MySortModel(self, sort_role=ROLE_SORT_ORDER) self.proxy.setSourceModel(self.std_model) self.setModel(self.proxy) self.setSortingEnabled(True) self.selectionModel().currentRowChanged.connect(self.item_changed) self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.update() def select_key(self, key): for i in range(self.model().rowCount()): item = self.model().index(i, self.Columns.DATE) row_key = item.data(ROLE_KEY) if key == row_key: self.selectionModel().setCurrentIndex( item, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) break def item_changed(self, idx: Optional[QModelIndex]): if idx is None: self.parent.receive_payreq_e.setText('') self.parent.receive_address_e.setText('') return if not idx.isValid(): return # TODO use siblingAtColumn when min Qt version is >=5.11 item = self.item_from_index(idx.sibling(idx.row(), self.Columns.DATE)) key = item.data(ROLE_KEY) req = self.wallet.get_request(key) if req is None: self.update() return self.parent.receive_payreq_e.setText( self.parent.wallet.get_request_URI(req)) self.parent.receive_address_e.setText(req.get_address()) self.parent.receive_payreq_e.repaint() # macOS hack (similar to #4777) self.parent.receive_address_e.repaint( ) # macOS hack (similar to #4777) def clearSelection(self): super().clearSelection() self.selectionModel().clearCurrentIndex() def refresh_status(self): m = self.std_model for r in range(m.rowCount()): idx = m.index(r, self.Columns.STATUS) date_idx = idx.sibling(idx.row(), self.Columns.DATE) date_item = m.itemFromIndex(date_idx) status_item = m.itemFromIndex(idx) key = date_item.data(ROLE_KEY) req = self.wallet.get_request(key) if req: status = self.parent.wallet.get_request_status(key) status_str = req.get_status_str(status) status_item.setText(status_str) status_item.setIcon(read_QIcon(pr_icons.get(status))) def update_item(self, key, invoice: Invoice): model = self.std_model for row in range(0, model.rowCount()): item = model.item(row, 0) if item.data(ROLE_KEY) == key: break else: return status_item = model.item(row, self.Columns.STATUS) status = self.parent.wallet.get_request_status(key) status_str = invoice.get_status_str(status) status_item.setText(status_str) status_item.setIcon(read_QIcon(pr_icons.get(status))) def update(self): # not calling maybe_defer_update() as it interferes with conditional-visibility self.parent.update_receive_address_styling() self.proxy.setDynamicSortFilter( False) # temp. disable re-sorting after every change self.std_model.clear() self.update_headers(self.__class__.headers) for req in self.wallet.get_unpaid_requests(): key = self.wallet.get_key_for_receive_request(req) status = self.parent.wallet.get_request_status(key) status_str = req.get_status_str(status) request_type = req.type timestamp = req.time amount = req.get_amount_sat() message = req.message date = format_time(timestamp) amount_str = self.parent.format_amount(amount) if amount else "" labels = [date, message, amount_str, status_str] icon = read_QIcon("dashcoin.png") tooltip = 'onchain request' items = [QStandardItem(e) for e in labels] self.set_editability(items) items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE) items[self.Columns.DATE].setData(key, ROLE_KEY) items[self.Columns.DATE].setData(timestamp, ROLE_SORT_ORDER) items[self.Columns.DATE].setIcon(icon) items[self.Columns.STATUS].setIcon(read_QIcon( pr_icons.get(status))) items[self.Columns.DATE].setToolTip(tooltip) self.std_model.insertRow(self.std_model.rowCount(), 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.receive_requests_label.setVisible(b) if not b: # list got hidden, so selected item should also be cleared: self.item_changed(None) def create_menu(self, position): items = self.selected_in_column(0) if len(items) > 1: keys = [item.data(ROLE_KEY) for item in items] menu = QMenu(self) menu.addAction(_("Delete requests"), lambda: self.parent.delete_requests(keys)) menu.exec_(self.viewport().mapToGlobal(position)) return idx = self.indexAt(position) # TODO use siblingAtColumn when min Qt version is >=5.11 item = self.item_from_index(idx.sibling(idx.row(), self.Columns.DATE)) if not item: return key = item.data(ROLE_KEY) req = self.wallet.get_request(key) if req is None: self.update() return menu = QMenu(self) self.add_copy_menu(menu, idx) URI = self.wallet.get_request_URI(req) menu.addAction(_("Copy Request"), lambda: self.parent.do_copy(URI, title='Dash URI')) menu.addAction( _("Copy Address"), lambda: self.parent.do_copy(req.get_address(), title='Dash Address')) #if 'view_url' in req: # menu.addAction(_("View in web browser"), lambda: webopen(req['view_url'])) menu.addAction(_("Delete"), lambda: self.parent.delete_requests([key])) run_hook('receive_list_menu', self.parent, menu, key) menu.exec_(self.viewport().mapToGlobal(position))
def select_storage( self, path, get_wallet_from_daemon) -> Tuple[str, Optional[WalletStorage]]: vbox = QVBoxLayout() hbox = QHBoxLayout() hbox.addWidget(QLabel(_('Wallet') + ':')) name_e = QLineEdit() hbox.addWidget(name_e) button = QPushButton(_('Choose...')) hbox.addWidget(button) vbox.addLayout(hbox) msg_label = WWLabel('') vbox.addWidget(msg_label) hbox2 = QHBoxLayout() pw_e = PasswordLineEdit('', self) pw_e.setFixedWidth(17 * char_width_in_lineedit()) pw_label = QLabel(_('Password') + ':') hbox2.addWidget(pw_label) hbox2.addWidget(pw_e) hbox2.addStretch() vbox.addLayout(hbox2) vbox.addSpacing(50) vbox_create_new = QVBoxLayout() vbox_create_new.addWidget(QLabel(_('Alternatively') + ':'), alignment=Qt.AlignLeft) button_create_new = QPushButton(_('Create New Wallet')) button_create_new.setMinimumWidth(120) vbox_create_new.addWidget(button_create_new, alignment=Qt.AlignLeft) widget_create_new = QWidget() widget_create_new.setLayout(vbox_create_new) vbox_create_new.setContentsMargins(0, 0, 0, 0) vbox.addWidget(widget_create_new) self.set_layout(vbox, title=_('Dash Electrum wallet')) temp_storage = None # type: Optional[WalletStorage] wallet_folder = os.path.dirname(path) def on_choose(): path, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) if path: name_e.setText(path) def on_filename(filename): # FIXME? "filename" might contain ".." (etc) and hence sketchy path traversals are possible nonlocal temp_storage temp_storage = None msg = None if filename: path = os.path.join(wallet_folder, filename) wallet_from_memory = get_wallet_from_daemon(path) try: if wallet_from_memory: temp_storage = wallet_from_memory.storage # type: Optional[WalletStorage] else: temp_storage = WalletStorage(path) except (StorageReadWriteError, WalletFileException) as e: msg = _('Cannot read file') + f'\n{repr(e)}' except Exception as e: self.logger.exception('') msg = _('Cannot read file') + f'\n{repr(e)}' else: msg = "" self.next_button.setEnabled(temp_storage is not None) user_needs_to_enter_password = False if temp_storage: if not temp_storage.file_exists(): msg =_("This file does not exist.") + '\n' \ + _("Press 'Next' to create this wallet, or choose another file.") elif not wallet_from_memory: if temp_storage.is_encrypted_with_user_pw(): msg = _("This file is encrypted with a password.") + '\n' \ + _('Enter your password or choose another file.') user_needs_to_enter_password = True elif temp_storage.is_encrypted_with_hw_device(): msg = _("This file is encrypted using a hardware device.") + '\n' \ + _("Press 'Next' to choose device to decrypt.") else: msg = _("Press 'Next' to open this wallet.") else: msg = _("This file is already open in memory.") + "\n" \ + _("Press 'Next' to create/focus window.") if msg is None: msg = _('Cannot read file') msg_label.setText(msg) widget_create_new.setVisible( bool(temp_storage and temp_storage.file_exists())) if user_needs_to_enter_password: pw_label.show() pw_e.show() pw_e.setFocus() else: pw_label.hide() pw_e.hide() button.clicked.connect(on_choose) button_create_new.clicked.connect( partial(name_e.setText, get_new_wallet_name(wallet_folder))) name_e.textChanged.connect(on_filename) name_e.setText(os.path.basename(path)) def run_user_interaction_loop(): while True: if self.loop.exec_() != 2: # 2 = next raise UserCancelled() assert temp_storage if temp_storage.file_exists( ) and not temp_storage.is_encrypted(): break if not temp_storage.file_exists(): break wallet_from_memory = get_wallet_from_daemon(temp_storage.path) if wallet_from_memory: raise WalletAlreadyOpenInMemory(wallet_from_memory) if temp_storage.file_exists() and temp_storage.is_encrypted(): if temp_storage.is_encrypted_with_user_pw(): password = pw_e.text() try: temp_storage.decrypt(password) break except InvalidPassword as e: self.show_message(title=_('Error'), msg=str(e)) continue except BaseException as e: self.logger.exception('') self.show_message(title=_('Error'), msg=repr(e)) raise UserCancelled() elif temp_storage.is_encrypted_with_hw_device(): try: self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET, storage=temp_storage) except InvalidPassword as e: self.show_message( title=_('Error'), msg= _('Failed to decrypt using this hardware device.' ) + '\n' + _('If you use a passphrase, make sure it is correct.' )) self.reset_stack() return self.select_storage(path, get_wallet_from_daemon) except (UserCancelled, GoBack): raise except BaseException as e: self.logger.exception('') self.show_message(title=_('Error'), msg=repr(e)) raise UserCancelled() if temp_storage.is_past_initial_decryption(): break else: raise UserCancelled() else: raise Exception('Unexpected encryption version') try: run_user_interaction_loop() finally: try: pw_e.clear() except RuntimeError: # wrapped C/C++ object has been deleted. pass # happens when decrypting with hw device return temp_storage.path, (temp_storage if temp_storage.file_exists() else None)
def description(self): return _("Send and receive payment requests via email")
def get_toolbar_buttons(self): return QLabel(_("Filter:")), self.change_button, self.used_button
def create_menu(self, position): from electrum_dash.wallet import Multisig_Wallet is_multisig = isinstance(self.wallet, Multisig_Wallet) can_delete = self.wallet.can_delete_address() selected = self.selectedItems() multi_select = len(selected) > 1 addrs = [item.text(1) for item in selected] if not addrs: return if not multi_select: item = self.itemAt(position) col = self.currentColumn() if not item: return addr = addrs[0] if not is_address(addr): item.setExpanded(not item.isExpanded()) return menu = QMenu() if not multi_select: column_title = self.headerItem().text(col) copy_text = item.text(col) menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(copy_text)) menu.addAction(_('Details'), lambda: self.parent.show_address(addr)) if col in self.editable_columns: menu.addAction(_("Edit {}").format(column_title), lambda: self.editItem(item, col)) menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr)) if self.wallet.can_export(): menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr)) if not is_multisig and not self.wallet.is_watching_only(): menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr)) menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr)) if can_delete: menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr)) addr_URL = block_explorer_URL(self.config, 'addr', addr) if addr_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(addr_URL)) if not self.wallet.is_frozen(addr): menu.addAction(_("Freeze"), lambda: self.parent.set_frozen_state([addr], True)) else: menu.addAction(_("Unfreeze"), lambda: self.parent.set_frozen_state([addr], False)) coins = self.wallet.get_utxos(addrs) if coins: menu.addAction(_("Spend from"), lambda: self.parent.spend_coins(coins)) run_hook('receive_menu', menu, addrs, self.wallet) menu.exec_(self.viewport().mapToGlobal(position))
def format_date(self, d): return str(datetime.date(d.year, d.month, d.day)) if d else _('None')
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> QVariant: # note: this method is performance-critical. # it is called a lot, and so must run extremely fast. assert index.isValid() col = index.column() tx_item = self.transactions.value_from_pos(index.row()) tx_hash = tx_item['txid'] conf = tx_item['confirmations'] txpos = tx_item['txpos_in_block'] or 0 height = tx_item['height'] islock = tx_item['islock'] try: status, status_str = self.tx_status_cache[tx_hash] except KeyError: tx_mined_info = self.tx_mined_info_from_tx_item(tx_item) status, status_str = self.parent.wallet.get_tx_status( tx_hash, tx_mined_info, islock) if role == Qt.UserRole: # for sorting d = { HistoryColumns.STATUS_ICON: # height breaks ties for unverified txns # txpos breaks ties for verified same block txns ((conf, -status, -height, -islock) if not conf and islock else (conf, -status, -height, -txpos)), HistoryColumns.STATUS_TEXT: status_str, HistoryColumns.DIP2: tx_item.get('dip2', ''), HistoryColumns.DESCRIPTION: tx_item['label'], HistoryColumns.COIN_VALUE: tx_item['value'].value, HistoryColumns.RUNNING_COIN_BALANCE: tx_item['balance'].value, HistoryColumns.FIAT_VALUE: tx_item['fiat_value'].value if 'fiat_value' in tx_item else None, HistoryColumns.FIAT_ACQ_PRICE: tx_item['acquisition_price'].value if 'acquisition_price' in tx_item else None, HistoryColumns.FIAT_CAP_GAINS: tx_item['capital_gain'].value if 'capital_gain' in tx_item else None, HistoryColumns.TXID: tx_hash, } return QVariant(d[col]) if role not in (Qt.DisplayRole, Qt.EditRole): if col == HistoryColumns.STATUS_ICON and role == Qt.DecorationRole: return QVariant(read_QIcon(TX_ICONS[status])) elif col == HistoryColumns.STATUS_ICON and role == Qt.ToolTipRole: c = str(conf) + _(' confirmation' + ('s' if conf != 1 else '')) if conf < 6 and islock: res = 'InstantSend, %s' % c else: res = c return QVariant(res) elif col != HistoryColumns.DESCRIPTION and role == Qt.TextAlignmentRole: return QVariant(Qt.AlignRight | Qt.AlignVCenter) elif col != HistoryColumns.DESCRIPTION and role == Qt.FontRole: monospace_font = QFont(MONOSPACE_FONT) return QVariant(monospace_font) elif col == HistoryColumns.DESCRIPTION and role == Qt.DecorationRole \ and self.parent.wallet.invoices.paid.get(tx_hash): return QVariant(read_QIcon("seal")) elif col == HistoryColumns.DIP2 and role == Qt.ForegroundRole: dip2_brush = QBrush(QColor("#1c75bc")) return QVariant(dip2_brush) elif col in (HistoryColumns.DESCRIPTION, HistoryColumns.COIN_VALUE) \ and role == Qt.ForegroundRole and tx_item['value'].value < 0: red_brush = QBrush(QColor("#BC1E1E")) return QVariant(red_brush) elif col == HistoryColumns.FIAT_VALUE and role == Qt.ForegroundRole \ and not tx_item.get('fiat_default') and tx_item.get('fiat_value') is not None: blue_brush = QBrush(QColor("#1E1EFF")) return QVariant(blue_brush) return QVariant() if col == HistoryColumns.STATUS_TEXT: return QVariant(status_str) elif col == HistoryColumns.DIP2: return QVariant(tx_item.get('dip2', '')) elif col == HistoryColumns.DESCRIPTION: return QVariant(tx_item['label']) elif col == HistoryColumns.COIN_VALUE: value = tx_item['value'].value v_str = self.parent.format_amount(value, is_diff=True, whitespaces=True) return QVariant(v_str) elif col == HistoryColumns.RUNNING_COIN_BALANCE: balance = tx_item['balance'].value balance_str = self.parent.format_amount(balance, whitespaces=True) return QVariant(balance_str) elif col == HistoryColumns.FIAT_VALUE and 'fiat_value' in tx_item: value_str = self.parent.fx.format_fiat(tx_item['fiat_value'].value) return QVariant(value_str) elif col == HistoryColumns.FIAT_ACQ_PRICE and \ tx_item['value'].value < 0 and 'acquisition_price' in tx_item: # fixme: should use is_mine acq = tx_item['acquisition_price'].value return QVariant(self.parent.fx.format_fiat(acq)) elif col == HistoryColumns.FIAT_CAP_GAINS and 'capital_gain' in tx_item: cg = tx_item['capital_gain'].value return QVariant(self.parent.fx.format_fiat(cg)) elif col == HistoryColumns.TXID: return QVariant(tx_hash) return QVariant()
def on_n(n): n_label.setText(_('From {0} cosigners').format(n)) cw.set_n(n) m_edit.setMaximum(n)
def done_processing_error(self, dialog, result): traceback.print_exception(*result, file=sys.stderr) dialog.show_error(_("Error synchronising labels") + ':\n' + str(result[:2]))
def done_processing_success(self, dialog, result): dialog.show_message(_("Your labels have been synchronised."))
def push(self, wallet): if not wallet.network: raise Exception(_('You are offline.')) return asyncio.run_coroutine_threadsafe( self.push_thread(wallet), wallet.network.asyncio_loop).result()
def settings_widget(self, window): return EnterButton(_('Settings'), partial(self.settings_dialog, window))
def decrypt_message(self, sequence, message, password): raise UserFacingException( _('Encryption and decryption are not implemented by {}').format( self.device))
from .seed_dialog import SeedLayout, KeysLayout from .network_dialog import NetworkChoiceLayout from .util import (MessageBoxMixin, Buttons, icon_path, ChoicesLayout, WWLabel, InfoButton, char_width_in_lineedit, PasswordLineEdit) from .password_dialog import PasswordLayout, PasswordLayoutForHW, PW_NEW from .bip39_recovery_dialog import Bip39RecoveryDialog from electrum_dash.plugin import run_hook, Plugins if TYPE_CHECKING: from electrum_dash.simple_config import SimpleConfig from electrum_dash.wallet_db import WalletDB from . import ElectrumGui MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\ + _("Leave this field empty if you want to disable encryption.") MSG_HW_STORAGE_ENCRYPTION = _("Set wallet file encryption.") + '\n'\ + _("Your wallet file does not contain secrets, mostly just metadata. ") \ + _("It also contains your master public key that allows watching your addresses.") + '\n\n'\ + _("Note: If you enable this setting, you will need your hardware device to open your wallet.") WIF_HELP_TEXT = ( _('WIF keys are typed in Dash Electrum, based on script type.') + '\n\n' + _('A few examples') + ':\n' + 'p2pkh:XERBBcaPf5D5... \t-> XhGqfhnL...\n') # note: full key is XERBBcaPf5D5oFXTEP7TdPWLem5ktc2Zr3AhhQhHVQaF49fDP6tN MSG_PASSPHRASE_WARN_ISSUE4566 = _("Warning") + ": "\ + _("You have multiple consecutive whitespaces or leading/trailing " "whitespaces in your passphrase.") + " " \ + _("This is discouraged.") + " " \ + _("Due to a bug, old versions of Dash Electrum will NOT be creating the "
def on_m(m): m_label.setText(_('Require {0} signatures').format(m)) cw.set_m(m) backup_warning_label.setVisible(cw.m != cw.n)
def receive_list_menu(self, menu, addr): window = get_parent_main_window(menu) menu.addAction(_("Send via e-mail"), lambda: self.send(window, addr))
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=_('Electrum-DASH 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 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: 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 '%s' contains multiple accounts, which are no longer supported since Electrum-DASH 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.storage.upgrade() 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 decrypt_message(self, sequence, message, password): raise RuntimeError(_('Encryption and decryption are not implemented by {}').format(self.device))
class InvoiceList(MyTreeView): class Columns(IntEnum): DATE = 0 REQUESTOR = 1 DESCRIPTION = 2 AMOUNT = 3 STATUS = 4 headers = { Columns.DATE: _('Expires'), Columns.REQUESTOR: _('Requestor'), Columns.DESCRIPTION: _('Description'), Columns.AMOUNT: _('Amount'), Columns.STATUS: _('Status'), } filter_columns = [Columns.DATE, Columns.REQUESTOR, Columns.DESCRIPTION, Columns.AMOUNT] def __init__(self, parent): super().__init__(parent, self.create_menu, stretch_column=self.Columns.DESCRIPTION, editable_columns=[]) self.setSortingEnabled(True) self.setColumnWidth(self.Columns.REQUESTOR, 200) self.setModel(QStandardItemModel(self)) self.update() 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) 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) def import_invoices(self): import_meta_gui(self.parent, _('invoices'), self.parent.invoices.import_file, self.update) def export_invoices(self): export_meta_gui(self.parent, _('invoices'), self.parent.invoices.export_file) def create_menu(self, position): idx = self.indexAt(position) item = self.model().itemFromIndex(idx) item_col0 = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE)) if not item or not item_col0: return key = item_col0.data(Qt.UserRole) column = idx.column() column_title = self.model().horizontalHeaderItem(column).text() column_data = item.text() status = self.parent.invoices.get_status(key) menu = QMenu(self) if column_data: menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) menu.addAction(_("Details"), lambda: self.parent.show_invoice(key)) if status == PR_UNPAID: menu.addAction(_("Pay Now"), lambda: self.parent.do_pay_invoice(key)) menu.addAction(_("Delete"), lambda: self.parent.delete_invoice(key)) menu.exec_(self.viewport().mapToGlobal(position))
def __str__(self): header = _("Error connecting to {} server").format('Labels') reason = self.reason if isinstance(reason, BaseException): reason = repr(reason) return f"{header}: {reason}" if reason else header
def import_invoices(self): import_meta_gui(self.parent, _('invoices'), self.parent.invoices.import_file, self.update)
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") % 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_dash.keystore 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())
def export_invoices(self): export_meta_gui(self.parent, _('invoices'), self.parent.invoices.export_file)
def on_m(m): m_label.setText(_('Require {0} signatures').format(m)) cw.set_m(m)
def derivation_and_script_type_gui_specific_dialog( self, *, title: str, message1: str, choices: List[Tuple[str, str, str]], hide_choices: bool = False, message2: str, test_text: Callable[[str], int], run_next, default_choice_idx: int = 0, get_account_xpub=None, ) -> Tuple[str, str]: vbox = QVBoxLayout() if get_account_xpub: button = QPushButton(_("Detect Existing Accounts")) def on_account_select(account): script_type = account["script_type"] if script_type == "p2pkh": script_type = "standard" button_index = c_values.index(script_type) button = clayout.group.buttons()[button_index] button.setChecked(True) line.setText(account["derivation_path"]) button.clicked.connect(lambda: Bip39RecoveryDialog( self, get_account_xpub, on_account_select)) vbox.addWidget(button, alignment=Qt.AlignLeft) vbox.addWidget(QLabel(_("Or"))) c_values = [x[0] for x in choices] c_titles = [x[1] for x in choices] c_default_text = [x[2] for x in choices] def on_choice_click(clayout): idx = clayout.selected_index() line.setText(c_default_text[idx]) clayout = ChoicesLayout(message1, c_titles, on_choice_click, checked_index=default_choice_idx) if not hide_choices: vbox.addLayout(clayout.layout()) vbox.addWidget(WWLabel(message2)) line = QLineEdit() def on_text_change(text): self.next_button.setEnabled(test_text(text)) line.textEdited.connect(on_text_change) on_choice_click(clayout) # set default text for "line" vbox.addWidget(line) self.exec_layout(vbox, title) choice = c_values[clayout.selected_index()] return str(line.text()), choice