def do_full_pull(self, force = False): try: connection = httplib.HTTPConnection(self.target_host) connection.request("GET", ("/api/wallets/%s/labels.json?auth_token=%s" % (self.wallet_id, self.auth_token())),"", {'Content-Type': 'application/json'}) response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return try: response = json.loads(response.read()) except ValueError as e: return False if "error" in response: QMessageBox.warning(None, _("Error"),_("Could not sync labels: %s" % response["error"])) return False for label in response: decoded_key = self.decode(label["external_id"]) decoded_label = self.decode(label["text"]) if force or not self.wallet.labels.get(decoded_key): self.wallet.labels[decoded_key] = decoded_label return True except socket.gaierror as e: print_error('Error connecting to service: %s ' % e) return False
def contextMenuEvent(self, e): m = self.createStandardContextMenu() if self.isReadOnly(): m.addAction(_("Show as QR code"), self.qr_show) else: m.addAction(_("Read QR code"), self.qr_input) m.exec_(e.globalPos())
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 seed_options(self): dialog = QDialog() vbox = QVBoxLayout(dialog) if 'ext' in self.options: cb_ext = QCheckBox(_('Extend this seed with custom words')) vbox.addWidget(cb_ext) if 'bip39' in self.options: def f(b): if b: msg = ' '.join([ '<b>' + _('Warning') + '</b>' + ': ', _('BIP39 seeds may not be supported in the future.'), '<br/><br/>', _('As technology matures, Litecoin address generation may change.'), _('However, BIP39 seeds do not include a version number.'), _('As a result, it is not possible to infer your wallet type from a BIP39 seed.'), '<br/><br/>', _('We do not guarantee that BIP39 seeds will be supported in future versions of Electrum.'), _('We recommend to use seeds generated by Electrum or compatible wallets.'), ]) #self.parent.show_warning(msg) self.seed_type_label.setVisible(not b) self.is_seed = (lambda x: bool(x)) if b else self.saved_is_seed self.on_edit() cb_bip39 = QCheckBox(_('BIP39 seed')) cb_bip39.toggled.connect(f) 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 network_dialog(self): grid = QGridLayout() grid.setSpacing(5) label = QLabel(_("Electrum communicates with remote servers to get information about your transactions and addresses. The servers all fulfil the same purpose only differing in hardware. In most cases you simply want to let Electrum pick one at random if you have a preference though feel free to select a server manually.") + "\n\n" \ + _("How do you want to connect to a server:")+" ") label.setWordWrap(True) grid.addWidget(label, 0, 0) gb = QGroupBox() b1 = QRadioButton(gb) b1.setText(_("Auto connect")) b1.setChecked(True) b2 = QRadioButton(gb) b2.setText(_("Select server manually")) grid.addWidget(b1,1,0) grid.addWidget(b2,2,0) vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addStretch(1) vbox.addLayout(Buttons(CancelButton(self), OkButton(self, _('Next')))) self.set_layout(vbox) if not self.exec_(): return if b2.isChecked(): return NetworkDialog(self.network, self.config, None).do_exec() else: self.config.set_key('auto_connect', True, True) return
def add_io(self, vbox): if self.tx.locktime > 0: vbox.addWidget(QLabel("LockTime: %d\n" % self.tx.locktime)) vbox.addWidget(QLabel(_("Inputs") + ' (%d)'%len(self.tx.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 def format_amount(amt): return self.main_window.format_amount(amt, whitespaces = True) 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)) if x.get('value'): cursor.insertText(format_amount(x['value']), ext) cursor.insertBlock() vbox.addWidget(i_text) vbox.addWidget(QLabel(_("Outputs") + ' (%d)'%len(self.tx.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(format_amount(v), ext) cursor.insertBlock() vbox.addWidget(o_text)
def enter_seed_box(is_restore, sid=None): vbox = QVBoxLayout() if is_restore: msg = _("Please enter your wallet seed, or master public key") + "\n" else: msg = _("Your seed is important!") \ + "\n" + _("To make sure that you have properly saved your seed, please retype it here.") logo = QLabel() logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56)) logo.setMaximumWidth(60) label = QLabel(msg) label.setWordWrap(True) seed_e = QTextEdit() seed_e.setMaximumHeight(100) vbox.addWidget(label) grid = QGridLayout() grid.addWidget(logo, 0, 0) grid.addWidget(seed_e, 0, 1) vbox.addLayout(grid) return vbox, seed_e
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 toggle_passphrase(self): if self.features.passphrase_protection: self.msg = _("Confirm on your %s device to disable passphrases") else: self.msg = _("Confirm on your %s device to enable passphrases") enabled = not self.features.passphrase_protection self.apply_settings(use_passphrase=enabled)
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 create_menu(self, position): self.selectedIndexes() item = self.currentItem() if not item: return column = self.currentColumn() tx_hash = str(item.data(0, Qt.UserRole).toString()) if not tx_hash: return if column is 0: column_title = "ID" column_data = tx_hash else: column_title = self.headerItem().text(column) column_data = item.text(column) tx_URL = block_explorer_URL(self.config, 'tx', tx_hash) height, conf, timestamp = self.wallet.get_tx_height(tx_hash) tx = self.wallet.transactions.get(tx_hash) is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) rbf = is_mine and height <=0 and tx and not tx.is_final() menu = QMenu() menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: menu.addAction(_("Edit %s")%column_title, lambda: self.editItem(item, column)) menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx)) if rbf: menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx)) if tx_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL)) menu.exec_(self.viewport().mapToGlobal(position))
def load_wallet_file(self, filename): try: storage = WalletStorage(filename) except Exception as e: QMessageBox.information(None, _('Error'), str(e), _('OK')) return if not storage.file_exists: recent = self.config.get('recently_open', []) if filename in recent: recent.remove(filename) self.config.set_key('recently_open', recent) action = 'new' else: try: wallet = Wallet(storage) except BaseException as e: traceback.print_exc(file=sys.stdout) QMessageBox.warning(None, _('Warning'), str(e), _('OK')) return action = wallet.get_action() # run wizard if action is not None: wizard = InstallWizard(self.app, self.config, self.network, storage) wallet = wizard.run(action) # keep current wallet if not wallet: return else: wallet.start_threads(self.network) return wallet
def qr_input(self): from electrum_ltc import qrscanner, get_config try: data = qrscanner.scan_qr(get_config()) except BaseException, e: QMessageBox.warning(self, _('Error'), _(e), _('OK')) return ""
def add_io(self, vbox): if self.tx.locktime > 0: vbox.addWidget(QLabel("LockTime: %d\n" % self.tx.locktime)) vbox.addWidget(QLabel(_("Inputs"))) def format_input(x): if x.get('is_coinbase'): return 'coinbase' else: _hash = x.get('prevout_hash') return _hash[0:16] + '...' + _hash[-8:] + ":%d"%x.get('prevout_n') + u'\t' + "%s"%x.get('address') lines = map(format_input, self.tx.inputs ) i_text = QTextEdit() i_text.setText('\n'.join(lines)) i_text.setReadOnly(True) i_text.setMaximumHeight(100) vbox.addWidget(i_text) vbox.addWidget(QLabel(_("Outputs"))) lines = map(lambda x: x[0] + u'\t\t' + self.parent.format_amount(x[1]) if x[1] else x[0], self.tx.get_outputs()) o_text = QTextEdit() o_text.setText('\n'.join(lines)) o_text.setReadOnly(True) o_text.setMaximumHeight(100) vbox.addWidget(o_text)
def add_cosigner_dialog(self, run_next, index, is_valid): title = _("Add Cosigner") + " %d"%index message = ' '.join([ _('Please enter the master public key (xpub) of your cosigner.'), _('Enter their master private key (xprv) if you want to be able to sign for them.') ]) return self.text_input(title, message, is_valid)
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 save(self): name = 'signed_%s.txn' % (self.tx.hash()[0:8]) if self.tx.is_complete() else 'unsigned.txn' fileName = self.parent.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 saved successfully"))
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_prefix = 'custom_secret_' self.was = _('Custom secret') else: self.filename_prefix = self.wallet_name + '_seed_' self.was = self.wallet_name + ' ' + _('seed') if self.extension: self.ext_warning(self.c_dialog) if not calibration: self.toPdf(QImage(cypherseed)) QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf'))) cypherseed.save(self.get_path_to_revealer_file('.png')) self.bcrypt(self.c_dialog) return cypherseed
def context_menu(self): view_menu = QMenu() themes_menu = view_menu.addMenu(_("&Themes")) selected_theme = self.actuator.selected_theme() theme_group = QActionGroup(self) for theme_name in self.actuator.theme_names(): theme_action = themes_menu.addAction(theme_name) theme_action.setCheckable(True) if selected_theme == theme_name: theme_action.setChecked(True) class SelectThemeFunctor: def __init__(self, theme_name, toggle_theme): self.theme_name = theme_name self.toggle_theme = toggle_theme def __call__(self, checked): if checked: self.toggle_theme(self.theme_name) delegate = SelectThemeFunctor(theme_name, self.toggle_theme) theme_action.toggled.connect(delegate) theme_group.addAction(theme_action) view_menu.addSeparator() show_receiving = view_menu.addAction(_("Show Receiving addresses")) show_receiving.setCheckable(True) show_receiving.toggled.connect(self.toggle_receiving_layout) show_receiving.setChecked(self.config.get("gui_show_receiving",False)) show_history = view_menu.addAction(_("Show History")) show_history.setCheckable(True) show_history.toggled.connect(self.show_history) show_history.setChecked(self.config.get("gui_show_history",False)) return view_menu
def task(): wallet.wait_until_synchronized() if wallet.is_found(): msg = _("Recovery successful") else: msg = _("No transactions found for this seed") self.emit(QtCore.SIGNAL('synchronized'), msg)
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-LTC"), self.close) self.tray.setContextMenu(m)
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 query_multisig(self, action): cw = CosignWidget(2, 2) m_edit = QSpinBox() n_edit = QSpinBox() m_edit.setValue(2) n_edit.setValue(2) n_edit.setMinimum(2) n_edit.setMaximum(15) m_edit.setMinimum(1) m_edit.setMaximum(2) n_edit.valueChanged.connect(m_edit.setMaximum) n_edit.valueChanged.connect(cw.set_n) m_edit.valueChanged.connect(cw.set_m) hbox = QHBoxLayout() hbox.addWidget(QLabel(_('Require'))) hbox.addWidget(m_edit) hbox.addWidget(QLabel(_('of'))) hbox.addWidget(n_edit) hbox.addWidget(QLabel(_('signatures'))) hbox.addStretch(1) vbox = QVBoxLayout() vbox.addWidget(cw) vbox.addWidget(WWLabel(_("Choose the number of signatures needed " "to unlock funds in your wallet:"))) vbox.addLayout(hbox) self.set_main_layout(vbox, _("Multi-Signature Wallet")) m = int(m_edit.value()) n = int(n_edit.value()) wallet_type = '%dof%d'%(m,n) return wallet_type
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 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 before_send(self): ''' Change URL to address before making a send. IMPORTANT: return False to continue execution of the send return True to stop execution of the send ''' if self.win.payto_e.is_multiline(): # only supports single line entries atm return False if self.win.payto_e.is_pr: return payto_e = str(self.win.payto_e.toPlainText()) regex = re.compile(r'^([^\s]+) <([A-Za-z0-9]+)>') # only do that for converted addresses try: (url, address) = regex.search(payto_e).groups() except AttributeError: return False if not self.validated: msgBox = QMessageBox() msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.')) msgBox.setInformativeText(_('Do you wish to continue?')) msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) msgBox.setDefaultButton(QMessageBox.Cancel) reply = msgBox.exec_() if reply != QMessageBox.Ok: return True return False
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 qr_input(self): from electrum_ltc import qrscanner try: data = qrscanner.scan_qr(self.win.config) except BaseException, e: QMessageBox.warning(self.win, _('Error'), _(e), _('OK')) return
def restore_or_create(self): vbox = QVBoxLayout() main_label = QLabel(_("Electrum could not find an existing wallet.")) vbox.addWidget(main_label) grid = QGridLayout() grid.setSpacing(5) gb1 = QGroupBox(_("What do you want to do?")) vbox.addWidget(gb1) b1 = QRadioButton(gb1) b1.setText(_("Create new wallet")) b1.setChecked(True) b2 = QRadioButton(gb1) b2.setText(_("Restore an existing wallet")) group1 = QButtonGroup() group1.addButton(b1) group1.addButton(b2) vbox.addWidget(b1) vbox.addWidget(b2) gb2 = QGroupBox(_("Wallet type:")) vbox.addWidget(gb2) group2 = QButtonGroup() self.wallet_types = [ ('standard', _("Standard wallet")), ('twofactor', _("Wallet with two-factor authentication")), ('multisig', _("Multi-signature wallet")), ('hardware', _("Hardware wallet")), ] for i, (wtype,name) in enumerate(self.wallet_types): if not filter(lambda x:x[0]==wtype, electrum.wallet.wallet_types): continue button = QRadioButton(gb2) button.setText(name) vbox.addWidget(button) group2.addButton(button) group2.setId(button, i) if i==0: button.setChecked(True) vbox.addStretch(1) hbox, button = ok_cancel_buttons2(self, _('Next')) vbox.addLayout(hbox) self.set_layout(vbox) self.show() self.raise_() button.setDefault(True) if not self.exec_(): return None, None action = 'create' if b1.isChecked() else 'restore' wallet_type = self.wallet_types[group2.checkedId()][0] return action, wallet_type
def notify_transactions(self, *dt): ''' ''' if not self.network or not self.network.is_connected(): return # temporarily disabled for merge return iface = self.network ptfn = iface.pending_transactions_for_notifications if len(ptfn) > 0: # Combine the transactions if there are more then three tx_amount = len(ptfn) if(tx_amount >= 3): total_amount = 0 for tx in ptfn: is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx) if(v > 0): total_amount += v self.notify(_("{txs}s new transactions received. Total amount" "received in the new transactions {amount}s" "{unit}s").format(txs=tx_amount, amount=self.format_amount(total_amount), unit=self.base_unit())) iface.pending_transactions_for_notifications = [] else: for tx in iface.pending_transactions_for_notifications: if tx: iface.pending_transactions_for_notifications.remove(tx) is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx) if(v > 0): self.notify( _("{txs} new transaction received. {amount} {unit}"). format(txs=tx_amount, amount=self.format_amount(v), unit=self.base_unit))
def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins)
def decrypt_message(self, sequence, message, password): raise RuntimeError( _('Encryption and decryption are not implemented by %s') % self.device)
def settings_widget(self, window): return EnterButton(_('Settings'), partial(self.settings_dialog, window))
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 description(self): return _("Send and receive payment requests via email")
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 push(self, wallet: 'Abstract_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 request_trezor_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) # KeepKey recovery doesn't need a word count if method == TIM_NEW or self.device == 'TREZOR': 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_ltc.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 sign_transaction(self, tx, password): if tx.is_complete(): return client = self.get_client() inputs = [] inputsPaths = [] pubKeys = [] chipInputs = [] redeemScripts = [] signatures = [] changePath = "" output = None p2shTransaction = False segwitTransaction = False pin = "" self.get_client( ) # prompt for the PIN before displaying the dialog if necessary # Fetch inputs of the transaction to sign derivations = self.get_tx_derivations(tx) for txin in tx.inputs(): if txin['type'] == 'coinbase': self.give_error( "Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True if txin['type'] in ['p2wpkh-p2sh', 'p2wsh-p2sh']: if not self.get_client_electrum().supports_segwit(): self.give_error(MSG_NEEDS_FW_UPDATE_SEGWIT) segwitTransaction = True if txin['type'] in ['p2wpkh', 'p2wsh']: if not self.get_client_electrum().supports_native_segwit(): self.give_error(MSG_NEEDS_FW_UPDATE_SEGWIT) segwitTransaction = True pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin) for i, x_pubkey in enumerate(x_pubkeys): if x_pubkey in derivations: signingPos = i s = derivations.get(x_pubkey) hwAddress = "%s/%d/%d" % (self.get_derivation()[2:], s[0], s[1]) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen redeemScript = Transaction.get_preimage_script(txin) txin_prev_tx = txin.get('prev_tx') if txin_prev_tx is None and not Transaction.is_segwit_input(txin): raise UserFacingException( _('Offline signing with {} is not supported for legacy inputs.' ).format(self.device)) txin_prev_tx_raw = txin_prev_tx.raw if txin_prev_tx else None inputs.append([ txin_prev_tx_raw, txin['prevout_n'], redeemScript, txin['prevout_hash'], signingPos, txin.get('sequence', 0xffffffff - 1), txin.get('value') ]) inputsPaths.append(hwAddress) pubKeys.append(pubkeys) # Sanity check if p2shTransaction: for txin in tx.inputs(): if txin['type'] != 'p2sh': self.give_error( "P2SH / regular input mixed in same transaction not supported" ) # should never happen txOutput = var_int(len(tx.outputs())) for o in tx.outputs(): output_type, addr, amount = o.type, o.address, o.value txOutput += int_to_hex(amount, 8) script = tx.pay_script(output_type, addr) txOutput += var_int(len(script) // 2) txOutput += script txOutput = bfh(txOutput) # Recognize outputs # - only one output and one change is authorized (for hw.1 and nano) # - at most one output can bypass confirmation (~change) (for all) if not p2shTransaction: if not self.get_client_electrum().supports_multi_output(): if len(tx.outputs()) > 2: self.give_error( "Transaction with more than 2 outputs not supported") has_change = False any_output_on_change_branch = is_any_tx_output_on_change_branch(tx) for o in tx.outputs(): assert o.type == TYPE_ADDRESS info = tx.output_info.get(o.address) if (info is not None) and len(tx.outputs()) > 1 \ and not has_change: index = info.address_index on_change_branch = index[0] == 1 # prioritise hiding outputs on the 'change' branch from user # because no more than one change address allowed if on_change_branch == any_output_on_change_branch: changePath = self.get_derivation( )[2:] + "/%d/%d" % index has_change = True else: output = o.address else: output = o.address self.handler.show_message( _("Confirm Transaction on your Ledger device...")) try: # Get trusted inputs from the original transactions for utxo in inputs: sequence = int_to_hex(utxo[5], 4) if segwitTransaction: tmp = bfh(utxo[3])[::-1] tmp += bfh(int_to_hex(utxo[1], 4)) tmp += bfh(int_to_hex(utxo[6], 8)) # txin['value'] chipInputs.append({ 'value': tmp, 'witness': True, 'sequence': sequence }) redeemScripts.append(bfh(utxo[2])) elif not p2shTransaction: txtmp = bitcoinTransaction(bfh(utxo[0])) trustedInput = self.get_client().getTrustedInput( txtmp, utxo[1]) trustedInput['sequence'] = sequence chipInputs.append(trustedInput) redeemScripts.append(txtmp.outputs[utxo[1]].script) else: tmp = bfh(utxo[3])[::-1] tmp += bfh(int_to_hex(utxo[1], 4)) chipInputs.append({'value': tmp, 'sequence': sequence}) redeemScripts.append(bfh(utxo[2])) # Sign all inputs firstTransaction = True inputIndex = 0 rawTx = tx.serialize_to_network() self.get_client().enableAlternate2fa(False) if segwitTransaction: self.get_client().startUntrustedTransaction( True, inputIndex, chipInputs, redeemScripts[inputIndex], version=tx.version) # we don't set meaningful outputAddress, amount and fees # as we only care about the alternateEncoding==True branch outputData = self.get_client().finalizeInput( b'', 0, 0, changePath, bfh(rawTx)) outputData['outputData'] = txOutput if outputData['confirmationNeeded']: outputData['address'] = output self.handler.finished() pin = self.handler.get_auth( outputData ) # does the authenticate dialog and returns pin if not pin: raise UserWarning() self.handler.show_message( _("Confirmed. Signing Transaction...")) while inputIndex < len(inputs): singleInput = [chipInputs[inputIndex]] self.get_client().startUntrustedTransaction( False, 0, singleInput, redeemScripts[inputIndex], version=tx.version) inputSignature = self.get_client().untrustedHashSign( inputsPaths[inputIndex], pin, lockTime=tx.locktime) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 else: while inputIndex < len(inputs): self.get_client().startUntrustedTransaction( firstTransaction, inputIndex, chipInputs, redeemScripts[inputIndex], version=tx.version) # we don't set meaningful outputAddress, amount and fees # as we only care about the alternateEncoding==True branch outputData = self.get_client().finalizeInput( b'', 0, 0, changePath, bfh(rawTx)) outputData['outputData'] = txOutput if outputData['confirmationNeeded']: outputData['address'] = output self.handler.finished() pin = self.handler.get_auth( outputData ) # does the authenticate dialog and returns pin if not pin: raise UserWarning() self.handler.show_message( _("Confirmed. Signing Transaction...")) else: # Sign input with the provided PIN inputSignature = self.get_client().untrustedHashSign( inputsPaths[inputIndex], pin, lockTime=tx.locktime) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 firstTransaction = False except UserWarning: self.handler.show_error(_('Cancelled by user')) return except BTChipException as e: if e.sw in (0x6985, 0x6d00): # cancelled by user return elif e.sw == 0x6982: raise # pin lock. decorator will catch it else: self.logger.exception('') self.give_error(e, True) except BaseException as e: self.logger.exception('') self.give_error(e, True) finally: self.handler.finished() for i, txin in enumerate(tx.inputs()): signingPos = inputs[i][4] tx.add_signature_to_txin(i, signingPos, bh2u(signatures[i])) tx.raw = tx.serialize()
def __init__(self, window, plugin, keystore, device_id): title = _("{} Settings").format(plugin.device) super(SettingsDialog, self).__init__(window, title) self.setMaximumWidth(540) devmgr = plugin.device_manager() config = devmgr.config handler = keystore.handler thread = keystore.thread hs_rows, hs_cols = (64, 128) def invoke_client(method, *args, **kw_args): unpair_after = kw_args.pop('unpair_after', False) def task(): client = devmgr.client_by_id(device_id) if not client: raise RuntimeError("Device not connected") if method: getattr(client, method)(*args, **kw_args) if unpair_after: devmgr.unpair_id(device_id) return client.features thread.add(task, on_success=update) def update(features): self.features = features set_label_enabled() if features.bootloader_hash: bl_hash = bh2u(features.bootloader_hash) bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]]) else: bl_hash = "N/A" noyes = [_("No"), _("Yes")] endis = [_("Enable Passphrases"), _("Disable Passphrases")] disen = [_("Disabled"), _("Enabled")] setchange = [_("Set a PIN"), _("Change PIN")] version = "%d.%d.%d" % (features.major_version, features.minor_version, features.patch_version) coins = ", ".join(coin.coin_name for coin in features.coins) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) coins_label.setText(coins) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language) def set_label_enabled(): label_apply.setEnabled(label_edit.text() != self.features.label) def rename(): invoke_client('change_label', label_edit.text()) def toggle_passphrase(): title = _("Confirm Toggle Passphrase Protection") currently_enabled = self.features.passphrase_protection if currently_enabled: msg = _("After disabling passphrases, you can only pair this " "Electrum wallet if it had an empty passphrase. " "If its passphrase was not empty, you will need to " "create a new wallet with the install wizard. You " "can use this wallet again at any time by re-enabling " "passphrases and entering its passphrase.") else: msg = _("Your current Electrum wallet can only be used with " "an empty passphrase. You must create a separate " "wallet with the install wizard for other passphrases " "as each one generates a new set of addresses.") msg += "\n\n" + _("Are you sure you want to proceed?") if not self.question(msg, title=title): return invoke_client('toggle_passphrase', unpair_after=currently_enabled) def change_homescreen(): dialog = QFileDialog(self, _("Choose Homescreen")) filename, __ = dialog.getOpenFileName() if filename.endswith('.toif'): img = open(filename, 'rb').read() if img[:8] != b'TOIf\x90\x00\x90\x00': raise Exception('File is not a TOIF file with size of 144x144') else: from PIL import Image # FIXME im = Image.open(filename) if im.size != (128, 64): raise Exception('Image must be 128 x 64 pixels') im = im.convert('1') pix = im.load() img = bytearray(1024) for j in range(64): for i in range(128): if pix[i, j]: o = (i + j * 128) img[o // 8] |= (1 << (7 - o % 8)) img = bytes(img) invoke_client('change_homescreen', img) def clear_homescreen(): invoke_client('change_homescreen', b'\x00') def set_pin(): invoke_client('set_pin', remove=False) def clear_pin(): invoke_client('set_pin', remove=True) def wipe_device(): wallet = window.wallet if wallet and sum(wallet.get_balance()): title = _("Confirm Device Wipe") msg = _("Are you SURE you want to wipe the device?\n" "Your wallet still has litecoins in it!") if not self.question(msg, title=title, icon=QMessageBox.Critical): return invoke_client('wipe_device', unpair_after=True) def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins) def slider_released(): config.set_session_timeout(timeout_slider.sliderPosition() * 60) # Information tab info_tab = QWidget() info_layout = QVBoxLayout(info_tab) info_glayout = QGridLayout() info_glayout.setColumnStretch(2, 1) device_label = QLabel() pin_set_label = QLabel() passphrases_label = QLabel() version_label = QLabel() device_id_label = QLabel() bl_hash_label = QLabel() bl_hash_label.setWordWrap(True) coins_label = QLabel() coins_label.setWordWrap(True) language_label = QLabel() initialized_label = QLabel() rows = [ (_("Device Label"), device_label), (_("PIN set"), pin_set_label), (_("Passphrases"), passphrases_label), (_("Firmware Version"), version_label), (_("Device ID"), device_id_label), (_("Bootloader Hash"), bl_hash_label), (_("Supported Coins"), coins_label), (_("Language"), language_label), (_("Initialized"), initialized_label), ] for row_num, (label, widget) in enumerate(rows): info_glayout.addWidget(QLabel(label), row_num, 0) info_glayout.addWidget(widget, row_num, 1) info_layout.addLayout(info_glayout) # Settings tab settings_tab = QWidget() settings_layout = QVBoxLayout(settings_tab) settings_glayout = QGridLayout() # Settings tab - Label label_msg = QLabel(_("Name this {}. If you have mutiple devices " "their labels help distinguish them.") .format(plugin.device)) label_msg.setWordWrap(True) label_label = QLabel(_("Device Label")) label_edit = QLineEdit() label_edit.setMinimumWidth(150) label_edit.setMaxLength(plugin.MAX_LABEL_LEN) label_apply = QPushButton(_("Apply")) label_apply.clicked.connect(rename) label_edit.textChanged.connect(set_label_enabled) settings_glayout.addWidget(label_label, 0, 0) settings_glayout.addWidget(label_edit, 0, 1, 1, 2) settings_glayout.addWidget(label_apply, 0, 3) settings_glayout.addWidget(label_msg, 1, 1, 1, -1) # Settings tab - PIN pin_label = QLabel(_("PIN Protection")) pin_button = QPushButton() pin_button.clicked.connect(set_pin) settings_glayout.addWidget(pin_label, 2, 0) settings_glayout.addWidget(pin_button, 2, 1) pin_msg = QLabel(_("PIN protection is strongly recommended. " "A PIN is your only protection against someone " "stealing your litecoins if they obtain physical " "access to your {}.").format(plugin.device)) pin_msg.setWordWrap(True) pin_msg.setStyleSheet("color: red") settings_glayout.addWidget(pin_msg, 3, 1, 1, -1) # Settings tab - Homescreen if plugin.device != 'KeepKey': # Not yet supported by KK firmware homescreen_layout = QHBoxLayout() homescreen_label = QLabel(_("Homescreen")) homescreen_change_button = QPushButton(_("Change...")) homescreen_clear_button = QPushButton(_("Reset")) homescreen_change_button.clicked.connect(change_homescreen) homescreen_clear_button.clicked.connect(clear_homescreen) homescreen_msg = QLabel(_("You can set the homescreen on your " "device to personalize it. You must " "choose a {} x {} monochrome black and " "white image.").format(hs_rows, hs_cols)) homescreen_msg.setWordWrap(True) settings_glayout.addWidget(homescreen_label, 4, 0) settings_glayout.addWidget(homescreen_change_button, 4, 1) settings_glayout.addWidget(homescreen_clear_button, 4, 2) settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1) # Settings tab - Session Timeout timeout_label = QLabel(_("Session Timeout")) timeout_minutes = QLabel() timeout_slider = QSlider(Qt.Horizontal) timeout_slider.setRange(1, 60) timeout_slider.setSingleStep(1) timeout_slider.setTickInterval(5) timeout_slider.setTickPosition(QSlider.TicksBelow) timeout_slider.setTracking(True) timeout_msg = QLabel( _("Clear the session after the specified period " "of inactivity. Once a session has timed out, " "your PIN and passphrase (if enabled) must be " "re-entered to use the device.")) timeout_msg.setWordWrap(True) timeout_slider.setSliderPosition(config.get_session_timeout() // 60) slider_moved() timeout_slider.valueChanged.connect(slider_moved) timeout_slider.sliderReleased.connect(slider_released) settings_glayout.addWidget(timeout_label, 6, 0) settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3) settings_glayout.addWidget(timeout_minutes, 6, 4) settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1) settings_layout.addLayout(settings_glayout) settings_layout.addStretch(1) # Advanced tab advanced_tab = QWidget() advanced_layout = QVBoxLayout(advanced_tab) advanced_glayout = QGridLayout() # Advanced tab - clear PIN clear_pin_button = QPushButton(_("Disable PIN")) clear_pin_button.clicked.connect(clear_pin) clear_pin_warning = QLabel( _("If you disable your PIN, anyone with physical access to your " "{} device can spend your litecoins.").format(plugin.device)) clear_pin_warning.setWordWrap(True) clear_pin_warning.setStyleSheet("color: red") advanced_glayout.addWidget(clear_pin_button, 0, 2) advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5) # Advanced tab - toggle passphrase protection passphrase_button = QPushButton() passphrase_button.clicked.connect(toggle_passphrase) passphrase_msg = WWLabel(PASSPHRASE_HELP) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") advanced_glayout.addWidget(passphrase_button, 3, 2) advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5) advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5) # Advanced tab - wipe device wipe_device_button = QPushButton(_("Wipe Device")) wipe_device_button.clicked.connect(wipe_device) wipe_device_msg = QLabel( _("Wipe the device, removing all data from it. The firmware " "is left unchanged.")) wipe_device_msg.setWordWrap(True) wipe_device_warning = QLabel( _("Only wipe a device if you have the recovery seed written down " "and the device wallet(s) are empty, otherwise the litecoins " "will be lost forever.")) wipe_device_warning.setWordWrap(True) wipe_device_warning.setStyleSheet("color: red") advanced_glayout.addWidget(wipe_device_button, 6, 2) advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5) advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5) advanced_layout.addLayout(advanced_glayout) advanced_layout.addStretch(1) tabs = QTabWidget(self) tabs.addTab(info_tab, _("Information")) tabs.addTab(settings_tab, _("Settings")) tabs.addTab(advanced_tab, _("Advanced")) dialog_vbox = QVBoxLayout(self) dialog_vbox.addWidget(tabs) dialog_vbox.addLayout(Buttons(CloseButton(self))) # Update information invoke_client(None)
def ignore_this_version(self): self.setText("") self.config.set_key("last_seen_version", self.latest_version, True) QMessageBox.information(self, _("Preference saved"), _("Notifications about this update will not be shown again.")) self.dialog.done(0)
import threading from PyQt5.Qt import Qt from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton from PyQt5.Qt import QVBoxLayout, QLabel from electrum_ltc_gui.qt.util import * from .trezor import TIM_NEW, TIM_RECOVER, TIM_MNEMONIC from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from electrum_ltc.i18n import _ from electrum_ltc.plugins import hook, DeviceMgr from electrum_ltc.util import PrintError, UserCancelled, bh2u from electrum_ltc.wallet import Wallet, Standard_Wallet PASSPHRASE_HELP_SHORT =_( "Passphrases allow you to access new wallets, each " "hidden behind a particular case-sensitive passphrase.") PASSPHRASE_HELP = PASSPHRASE_HELP_SHORT + " " + _( "You need to create a separate Electrum wallet for each passphrase " "you use as they each generate different addresses. Changing " "your passphrase does not lose other wallets, each is still " "accessible behind its own passphrase.") RECOMMEND_PIN = _( "You should enable PIN protection. Your PIN is the only protection " "for your litecoins if your device is lost or stolen.") PASSPHRASE_NOT_PIN = _( "If you forget a passphrase you will be unable to access any " "litecoins in the wallet behind it. A passphrase is not a PIN. " "Only change this if you are sure you understand it.") CHARACTER_RECOVERY = ( "Use the recovery cipher shown on your device to input your seed words. "
def on_copy(self): self.app.clipboard().setText(self.text()) QToolTip.showText(QCursor.pos(), _("Text copied to clipboard"), self)
def ignore_all_version(self): self.setText("") self.config.set_key("last_seen_version", "9.9.9", True) QMessageBox.information(self, _("Preference saved"), _("No more notifications about version updates will be shown.")) self.dialog.done(0)
def show_message(self, msg, parent=None, title=None): return self.msg_box(QMessageBox.Information, parent, title or _('Information'), msg)
def new_electrum_version(self): if self.new_version: self.setText(_("New version available") + ": " + self.latest_version) self.sb.insertPermanentWidget(1, self)
def show_error(self, msg, parent=None): return self.msg_box(QMessageBox.Warning, parent, _('Error'), msg)
def addCopyButton(self, app): self.app = app self.addButton(":icons/copy.png", self.on_copy, _("Copy to clipboard"))
def __init__(self, dialog, label=None): QPushButton.__init__(self, label or _("Cancel")) self.clicked.connect(dialog.reject)
def show_critical(self, msg, parent=None, title=None): return self.msg_box(QMessageBox.Critical, parent, title or _('Critical Error'), msg)
def __init__(self, text_getter, app, dialog): QPushButton.__init__(self, _("Copy and Close")) self.clicked.connect(lambda: app.clipboard().setText(text_getter())) self.clicked.connect(dialog.close) self.setDefault(True)
def show_warning(self, msg, parent=None, title=None): return self.msg_box(QMessageBox.Warning, parent, title or _('Warning'), msg)
def __init__(self, dialog): QPushButton.__init__(self, _("Close")) self.clicked.connect(dialog.close) self.setDefault(True)
def __init__(self, dialog, label=None): QPushButton.__init__(self, label or _("OK")) self.clicked.connect(dialog.accept) self.setDefault(True)
def verify_seed(self, seed, is_valid=None): while True: r = self.request_seed(MSG_VERIFY_SEED, is_valid) if prepare_seed(r) == prepare_seed(seed): return self.show_error(_('Incorrect seed'))
def __init__(self, text_getter, app): QPushButton.__init__(self, _("Copy")) self.clicked.connect(lambda: app.clipboard().setText(text_getter()))
def done_processing(self, dialog, result): dialog.show_message(_("Your labels have been synchronised."))
MONOSPACE_FONT = 'Monaco' else: MONOSPACE_FONT = 'monospace' dialogs = [] from electrum_ltc.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED pr_icons = { PR_UNPAID: ":icons/unpaid.png", PR_PAID: ":icons/confirmed.png", PR_EXPIRED: ":icons/expired.png" } pr_tooltips = { PR_UNPAID: _('Pending'), PR_PAID: _('Paid'), PR_EXPIRED: _('Expired') } expiration_values = [(_('1 hour'), 60 * 60), (_('1 day'), 24 * 60 * 60), (_('1 week'), 7 * 24 * 60 * 60), (_('Never'), None)] class Timer(QThread): stopped = False timer_signal = pyqtSignal() def run(self): while not self.stopped: self.timer_signal.emit()
def addCopyButton(self, app): self.app = app f = lambda: self.app.clipboard().setText(str(self.text())) self.addButton(":icons/copy.png", f, _("Copy to clipboard"))
def __init__(self, window: 'ElectrumWindow', chan_id: bytes): super().__init__(window) # initialize instance fields self.window = window self.wallet = window.wallet chan = self.chan = window.wallet.lnworker.channels[chan_id] self.format_msat = lambda msat: window.format_amount_and_units(msat / 1000) # connect signals with slots self.htlc_fulfilled.connect(self.on_htlc_fulfilled) self.htlc_failed.connect(self.on_htlc_failed) self.state_changed.connect(self.do_state_changed) self.htlc_added.connect(self.on_htlc_added) # register callbacks for updating util.register_callback(self.htlc_fulfilled.emit, ['htlc_fulfilled']) util.register_callback(self.htlc_failed.emit, ['htlc_failed']) util.register_callback(self.htlc_added.emit, ['htlc_added']) util.register_callback(self.state_changed.emit, ['channel']) # set attributes of QDialog self.setWindowTitle(_('Channel Details')) self.setMinimumSize(800, 400) # add layouts vbox = QtWidgets.QVBoxLayout(self) vbox.addWidget(QLabel(_('Remote Node ID:'))) remote_id_e = ButtonsLineEdit(bh2u(chan.node_id)) remote_id_e.addCopyButton(self.window.app) remote_id_e.setReadOnly(True) vbox.addWidget(remote_id_e) funding_label_text = f'<a href=click_destination>{chan.funding_outpoint.txid}</a>:{chan.funding_outpoint.output_index}' vbox.addWidget(QLabel(_('Funding Outpoint:'))) vbox.addWidget(LinkedLabel(funding_label_text, self.show_tx)) form_layout = QtWidgets.QFormLayout(None) # add form content form_layout.addRow( _('Channel ID:'), SelectableLabel( f"{chan.channel_id.hex()} (Short: {chan.short_channel_id})")) form_layout.addRow(_('State:'), SelectableLabel(chan.get_state_for_GUI())) self.initiator = 'Local' if chan.constraints.is_initiator else 'Remote' form_layout.addRow(_('Initiator:'), SelectableLabel(self.initiator)) self.capacity = self.window.format_amount_and_units( chan.get_capacity()) form_layout.addRow(_('Capacity:'), SelectableLabel(self.capacity)) self.can_send_label = SelectableLabel() self.can_receive_label = SelectableLabel() form_layout.addRow(_('Can send:'), self.can_send_label) form_layout.addRow(_('Can receive:'), self.can_receive_label) self.received_label = SelectableLabel() form_layout.addRow(_('Received:'), self.received_label) self.sent_label = SelectableLabel() form_layout.addRow(_('Sent:'), self.sent_label) #self.htlc_minimum_msat = SelectableLabel(str(chan.config[REMOTE].htlc_minimum_msat)) #form_layout.addRow(_('Minimum HTLC value accepted by peer (mSAT):'), self.htlc_minimum_msat) #self.max_htlcs = SelectableLabel(str(chan.config[REMOTE].max_accepted_htlcs)) #form_layout.addRow(_('Maximum number of concurrent HTLCs accepted by peer:'), self.max_htlcs) #self.max_htlc_value = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].max_htlc_value_in_flight_msat / 1000)) #form_layout.addRow(_('Maximum value of in-flight HTLCs accepted by peer:'), self.max_htlc_value) self.dust_limit = SelectableLabel( self.window.format_amount_and_units( chan.config[REMOTE].dust_limit_sat)) form_layout.addRow(_('Remote dust limit:'), self.dust_limit) self.remote_reserve = self.window.format_amount_and_units( chan.config[REMOTE].reserve_sat) form_layout.addRow(_('Remote reserve:'), SelectableLabel(self.remote_reserve)) vbox.addLayout(form_layout) # add htlc tree view to vbox (wouldn't scale correctly in QFormLayout) vbox.addWidget(QLabel(_('Payments (HTLCs):'))) w = QtWidgets.QTreeView(self) htlc_dict = chan.get_payments() htlc_list = [] for rhash, _list in htlc_dict.items(): for _tuple in _list: htlc_list.append((rhash.hex(), ) + _tuple) w.setModel(self.make_model(htlc_list)) w.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) vbox.addWidget(w) vbox.addLayout(Buttons(CloseButton(self))) # initialize sent/received fields self.update()