def __init__(self, parent=None): MyTreeWidget.__init__(self, parent, self.create_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1) self.setSelectionMode(QAbstractItemView.ExtendedSelection)
def create_menu(self, position): from electrum_axe.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 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() for address in addr_list: num = len(self.wallet.get_address_history(address)) is_used = self.wallet.is_used(address) label = self.wallet.labels.get(address, '') c, u, x = self.wallet.get_addr_balance(address) balance = c + u + x if self.show_used == 1 and (balance or is_used): continue if self.show_used == 2 and balance == 0: continue if self.show_used == 3 and not is_used: continue balance_text = self.parent.format_amount(balance, whitespaces=True) fx = self.parent.fx # 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)
from PyQt5.Qt import Qt from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton from PyQt5.Qt import QVBoxLayout, QLabel from electrum_axe.gui.qt.util import * from electrum_axe.i18n import _ from electrum_axe.plugin import hook, DeviceMgr from electrum_axe.util import PrintError, UserCancelled, bh2u from electrum_axe.wallet import Wallet, Standard_Wallet from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from .safe_t import SafeTPlugin, TIM_NEW, TIM_RECOVER, TIM_MNEMONIC PASSPHRASE_HELP_SHORT =_( "Passphrases allow you to access new wallets, each " "hidden behind a particular case-sensitive passphrase.") PASSPHRASE_HELP = PASSPHRASE_HELP_SHORT + " " + _( "You need to create a separate AXE 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 AXE coins if your device is lost or stolen.") PASSPHRASE_NOT_PIN = _( "If you forget a passphrase you will be unable to access any " "AXE coins in the wallet behind it. A passphrase is not a PIN. " "Only change this if you are sure you understand it.")
def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins)
def contextMenuEvent(self, e): m = self.createStandardContextMenu() m.addAction(_("Read QR code"), self.qr_input) m.exec_(e.globalPos())
from electrum_axe.i18n import _ fullname = 'Digital Bitbox' description = _('Provides support for Digital Bitbox hardware wallet') registers_keystore = ('hardware', 'digitalbitbox', _("Digital Bitbox wallet")) available_for = ['qt', 'cmdline']
def decrypt_message(self, pubkey, message, password): raise RuntimeError( _('Encryption and decryption are currently not supported for {}'). format(self.device))
def sign_message(self, sequence, message, password): sig = None try: message = message.encode('utf8') inputPath = self.get_derivation() + "/%d/%d" % sequence msg_hash = sha256d(msg_magic(message)) inputHash = to_hexstr(msg_hash) hasharray = [] hasharray.append({'hash': inputHash, 'keypath': inputPath}) hasharray = json.dumps(hasharray) msg = ('{"sign":{"meta":"sign message", "data":%s}}' % hasharray).encode('utf8') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception(_("Could not sign message.")) reply = dbb_client.hid_send_encrypt(msg) self.handler.show_message( _("Signing message ...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds." ) + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout." )) reply = dbb_client.hid_send_encrypt( msg ) # Send twice, first returns an echo for smart verification (not implemented) self.handler.finished() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception(_("Could not sign message.")) if 'recid' in reply['sign'][0]: # firmware > v2.1.1 sig_string = binascii.unhexlify(reply['sign'][0]['sig']) recid = int(reply['sign'][0]['recid'], 16) sig = ecc.construct_sig65(sig_string, recid, True) pubkey, compressed = ecc.ECPubkey.from_signature65( sig, msg_hash) addr = public_key_to_p2pkh( pubkey.get_public_key_bytes(compressed=compressed)) if ecc.verify_message_with_address(addr, sig, message) is False: raise Exception(_("Could not sign message")) elif 'pubkey' in reply['sign'][0]: # firmware <= v2.1.1 for recid in range(4): sig_string = binascii.unhexlify(reply['sign'][0]['sig']) sig = ecc.construct_sig65(sig_string, recid, True) try: addr = public_key_to_p2pkh( binascii.unhexlify(reply['sign'][0]['pubkey'])) if ecc.verify_message_with_address(addr, sig, message): break except Exception: continue else: raise Exception(_("Could not sign message")) except BaseException as e: self.give_error(e) return sig
def format_date(self, d): return str(datetime.date(d.year, d.month, d.day)) if d else _('None')
def select_import_file(): text = QFileDialog.getOpenFileName(None, _('Select a file to import'), '', '*.conf') if text and len(text) == 2: import_filename_edit.setText(text[0])
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 now = int(time.time()) d = { HistoryColumns.STATUS_ICON: # height breaks ties for unverified txns # txpos breaks ties for verified same block txns ((conf, now - islock + 1) # one more to be gt -status (-0) 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 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 __init__(self, text=None): ButtonsTextEdit.__init__(self, text) self.setReadOnly(1) self.addButton(":icons/qrcode.png", self.qr_show, _("Show as QR code")) run_hook('show_text_edit', self)
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2pkhTransaction = True derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error( "Coinbase not supported") # should never happen if txin['type'] != 'p2pkh': p2pkhTransaction = False for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = sha256d( binascii.unhexlify(tx.serialize_preimage(i))) hasharray_i = { 'hash': to_hexstr(inputHash), 'keypath': inputPath } hasharray.append(hasharray_i) inputhasharray.append(inputHash) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen # Build pubkeyarray from outputs for o in tx.outputs(): assert o.type == TYPE_ADDRESS info = tx.output_info.get(o.address) if info is not None: index = info.address_index changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = { 'pubkey': changePubkey, 'keypath': changePath } pubkeyarray.append(pubkeyarray_i) # Special serialization of the unsigned transaction for # the mobile verification app. # At the moment, verification only works for p2pkh transactions. if p2pkhTransaction: class CustomTXSerialization(Transaction): @classmethod def input_script(self, txin, estimate_size=False): if txin['type'] == 'p2pkh': return Transaction.get_preimage_script(txin) if txin['type'] == 'p2sh': # Multisig verification has partial support, but is disabled. This is the # expected serialization though, so we leave it here until we activate it. return '00' + push_script( Transaction.get_preimage_script(txin)) raise Exception("unsupported type %s" % txin['type']) tx_dbb_serialized = CustomTXSerialization( tx.serialize()).serialize_to_network() else: # We only need this for the signing echo / verification. tx_dbb_serialized = None # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs:(step + 1) * self.maxInputs] msg = { "sign": { "data": hashes, "checkpub": pubkeyarray, }, } if tx_dbb_serialized is not None: msg["sign"]["meta"] = to_hexstr(sha256d(tx_dbb_serialized)) msg = json.dumps(msg).encode('ascii') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") if self.plugin.is_mobile_paired( ) and tx_dbb_serialized is not None: reply['tx'] = tx_dbb_serialized self.plugin.comserver_post_notification(reply) if steps > 1: self.handler.show_message( _("Signing large transaction. Please be patient ...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds." ) + " " + _("(Touch {} of {})").format((step + 1), steps) + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout." ) + "\n\n") else: self.handler.show_message( _("Signing transaction...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds." ) + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout." )) # Send twice, first returns an echo for smart verification reply = dbb_client.hid_send_encrypt(msg) self.handler.finished() if 'error' in reply: if reply["error"].get('code') in (600, 601): # aborted via LED short touch or timeout raise UserCancelled() raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) != len(tx.inputs()): raise Exception("Incorrect number of transactions signed." ) # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = list(filter(None, txin['signatures'])) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if 'recid' in signed: # firmware > v2.1.1 recid = int(signed['recid'], 16) s = binascii.unhexlify(signed['sig']) h = inputhasharray[i] pk = ecc.ECPubkey.from_sig_string(s, recid, h) pk = pk.get_public_key_hex(compressed=True) elif 'pubkey' in signed: # firmware <= v2.1.1 pk = signed['pubkey'] if pk != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = ecc.der_sig_from_r_and_s(sig_r, sig_s) sig = to_hexstr(sig) + '01' tx.add_signature_to_txin(i, ii, sig) except UserCancelled: raise except BaseException as e: self.give_error(e, True) else: _logger.info("Transaction is_complete {tx.is_complete()}") tx.raw = tx.serialize()
def contextMenuEvent(self, e): m = self.createStandardContextMenu() m.addAction(_("Show as QR code"), self.qr_show) m.exec_(e.globalPos())
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))
def done_processing_success(self, dialog, result): dialog.show_message(_("Your labels have been synchronised."))
def decrypt_message(self, sequence, message, password): raise RuntimeError( _('Encryption and decryption are not implemented by {}').format( self.device))
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 __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) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language) def set_label_enabled(): label_apply.setEnabled(label_edit.text() != self.features.label) def rename(): invoke_client('change_label', label_edit.text()) def toggle_passphrase(): title = _("Confirm Toggle Passphrase Protection") currently_enabled = self.features.passphrase_protection if currently_enabled: msg = _("After disabling passphrases, you can only pair this " "AXE 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 AXE 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 not filename: return # user cancelled if filename.endswith('.toif'): img = open(filename, 'rb').read() if img[:8] != b'TOIf\x90\x00\x90\x00': handler.show_error('File is not a TOIF file with size of 144x144') return else: from PIL import Image # FIXME im = Image.open(filename) if im.size != (128, 64): handler.show_error('Image must be 128 x 64 pixels') return im = im.convert('1') pix = im.load() img = bytearray(1024) for j in range(64): for i in range(128): if pix[i, j]: o = (i + j * 128) img[o // 8] |= (1 << (7 - o % 8)) img = bytes(img) invoke_client('change_homescreen', img) def clear_homescreen(): invoke_client('change_homescreen', b'\x00') def set_pin(): invoke_client('set_pin', remove=False) def clear_pin(): invoke_client('set_pin', remove=True) def wipe_device(): wallet = window.wallet if wallet and sum(wallet.get_balance()): title = _("Confirm Device Wipe") msg = _("Are you SURE you want to wipe the device?\n" "Your wallet still has AXE coins in it!") if not self.question(msg, title=title, icon=QMessageBox.Critical): return invoke_client('wipe_device', unpair_after=True) def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins) def slider_released(): config.set_session_timeout(timeout_slider.sliderPosition() * 60) # Information tab info_tab = QWidget() info_layout = QVBoxLayout(info_tab) info_glayout = QGridLayout() info_glayout.setColumnStretch(2, 1) device_label = QLabel() pin_set_label = QLabel() passphrases_label = QLabel() version_label = QLabel() device_id_label = QLabel() bl_hash_label = QLabel() bl_hash_label.setWordWrap(True) language_label = QLabel() initialized_label = QLabel() rows = [ (_("Device Label"), device_label), (_("PIN set"), pin_set_label), (_("Passphrases"), passphrases_label), (_("Firmware Version"), version_label), (_("Device ID"), device_id_label), (_("Bootloader Hash"), bl_hash_label), (_("Language"), language_label), (_("Initialized"), initialized_label), ] for row_num, (label, widget) in enumerate(rows): info_glayout.addWidget(QLabel(label), row_num, 0) info_glayout.addWidget(widget, row_num, 1) info_layout.addLayout(info_glayout) # Settings tab settings_tab = QWidget() settings_layout = QVBoxLayout(settings_tab) settings_glayout = QGridLayout() # Settings tab - Label label_msg = QLabel(_("Name this {}. If you have multiple devices " "their labels help distinguish them.") .format(plugin.device)) label_msg.setWordWrap(True) label_label = QLabel(_("Device Label")) label_edit = QLineEdit() label_edit.setMinimumWidth(150) label_edit.setMaxLength(plugin.MAX_LABEL_LEN) label_apply = QPushButton(_("Apply")) label_apply.clicked.connect(rename) label_edit.textChanged.connect(set_label_enabled) settings_glayout.addWidget(label_label, 0, 0) settings_glayout.addWidget(label_edit, 0, 1, 1, 2) settings_glayout.addWidget(label_apply, 0, 3) settings_glayout.addWidget(label_msg, 1, 1, 1, -1) # Settings tab - PIN pin_label = QLabel(_("PIN Protection")) pin_button = QPushButton() pin_button.clicked.connect(set_pin) settings_glayout.addWidget(pin_label, 2, 0) settings_glayout.addWidget(pin_button, 2, 1) pin_msg = QLabel(_("PIN protection is strongly recommended. " "A PIN is your only protection against someone " "stealing your AXE coins if they obtain physical " "access to your {}.").format(plugin.device)) pin_msg.setWordWrap(True) pin_msg.setStyleSheet("color: red") settings_glayout.addWidget(pin_msg, 3, 1, 1, -1) # Settings tab - Homescreen homescreen_label = QLabel(_("Homescreen")) homescreen_change_button = QPushButton(_("Change...")) homescreen_clear_button = QPushButton(_("Reset")) homescreen_change_button.clicked.connect(change_homescreen) try: import PIL except ImportError: homescreen_change_button.setDisabled(True) homescreen_change_button.setToolTip( _("Required package 'PIL' is not available - Please install it.") ) homescreen_clear_button.clicked.connect(clear_homescreen) homescreen_msg = QLabel(_("You can set the homescreen on your " "device to personalize it. You must " "choose a {} x {} monochrome black and " "white image.").format(hs_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 AXE coins.").format(plugin.device)) clear_pin_warning.setWordWrap(True) clear_pin_warning.setStyleSheet("color: red") advanced_glayout.addWidget(clear_pin_button, 0, 2) advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5) # Advanced tab - toggle passphrase protection passphrase_button = QPushButton() passphrase_button.clicked.connect(toggle_passphrase) passphrase_msg = WWLabel(PASSPHRASE_HELP) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") advanced_glayout.addWidget(passphrase_button, 3, 2) advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5) advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5) # Advanced tab - wipe device wipe_device_button = QPushButton(_("Wipe Device")) wipe_device_button.clicked.connect(wipe_device) wipe_device_msg = QLabel( _("Wipe the device, removing all data from it. The firmware " "is left unchanged.")) wipe_device_msg.setWordWrap(True) wipe_device_warning = QLabel( _("Only wipe a device if you have the recovery seed written down " "and the device wallet(s) are empty, otherwise the AXE coins " "will be lost forever.")) wipe_device_warning.setWordWrap(True) wipe_device_warning.setStyleSheet("color: red") advanced_glayout.addWidget(wipe_device_button, 6, 2) advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5) advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5) advanced_layout.addLayout(advanced_glayout) advanced_layout.addStretch(1) tabs = QTabWidget(self) tabs.addTab(info_tab, _("Information")) tabs.addTab(settings_tab, _("Settings")) tabs.addTab(advanced_tab, _("Advanced")) dialog_vbox = QVBoxLayout(self) dialog_vbox.addWidget(tabs) dialog_vbox.addLayout(Buttons(CloseButton(self))) # Update information invoke_client(None)
def __init__(self, network, config, wizard=False): self.network = network self.config = config self.protocol = None self.tor_proxy = None self.tabs = tabs = QTabWidget() server_tab = QWidget() proxy_tab = QWidget() blockchain_tab = QWidget() tabs.addTab(blockchain_tab, _('Overview')) tabs.addTab(server_tab, _('Server')) tabs.addTab(proxy_tab, _('Proxy')) # server tab grid = QGridLayout(server_tab) grid.setSpacing(8) self.server_host = QLineEdit() self.server_host.setFixedWidth(200) self.server_port = QLineEdit() self.server_port.setFixedWidth(60) self.autoconnect_cb = QCheckBox(_('Select server automatically')) self.autoconnect_cb.setEnabled( self.config.is_modifiable('auto_connect')) self.server_host.editingFinished.connect(self.set_server) self.server_port.editingFinished.connect(self.set_server) self.autoconnect_cb.clicked.connect(self.set_server) self.autoconnect_cb.clicked.connect(self.update) msg = ' '.join([ _("If auto-connect is enabled, Electrum-AXE will always use a server that is on the longest blockchain." ), _("If it is disabled, you have to choose a server you want to use. Electrum-AXE will warn you if your server is lagging." ) ]) grid.addWidget(self.autoconnect_cb, 0, 0, 1, 3) grid.addWidget(HelpButton(msg), 0, 4) grid.addWidget(QLabel(_('Server') + ':'), 1, 0) grid.addWidget(self.server_host, 1, 1, 1, 2) grid.addWidget(self.server_port, 1, 3) label = _('Server peers') if network.is_connected() else _( 'Default Servers') grid.addWidget(QLabel(label), 2, 0, 1, 5) self.servers_list = ServerListWidget(self) grid.addWidget(self.servers_list, 3, 0, 1, 5) # Proxy tab grid = QGridLayout(proxy_tab) grid.setSpacing(8) # proxy setting self.proxy_cb = QCheckBox(_('Use proxy')) self.proxy_cb.clicked.connect(self.check_disable_proxy) self.proxy_cb.clicked.connect(self.set_proxy) self.proxy_mode = QComboBox() self.proxy_mode.addItems(['SOCKS4', 'SOCKS5', 'HTTP']) self.proxy_host = QLineEdit() self.proxy_host.setFixedWidth(200) self.proxy_port = QLineEdit() self.proxy_port.setFixedWidth(60) self.proxy_user = QLineEdit() self.proxy_user.setPlaceholderText(_("Proxy user")) self.proxy_password = QLineEdit() self.proxy_password.setPlaceholderText(_("Password")) self.proxy_password.setEchoMode(QLineEdit.Password) self.proxy_password.setFixedWidth(60) self.proxy_mode.currentIndexChanged.connect(self.set_proxy) self.proxy_host.editingFinished.connect(self.set_proxy) self.proxy_port.editingFinished.connect(self.set_proxy) self.proxy_user.editingFinished.connect(self.set_proxy) self.proxy_password.editingFinished.connect(self.set_proxy) self.proxy_mode.currentIndexChanged.connect( self.proxy_settings_changed) self.proxy_host.textEdited.connect(self.proxy_settings_changed) self.proxy_port.textEdited.connect(self.proxy_settings_changed) self.proxy_user.textEdited.connect(self.proxy_settings_changed) self.proxy_password.textEdited.connect(self.proxy_settings_changed) self.tor_cb = QCheckBox(_("Use Tor Proxy")) self.tor_cb.setIcon(QIcon(":icons/tor_logo.png")) self.tor_cb.hide() self.tor_cb.clicked.connect(self.use_tor_proxy) grid.addWidget(self.tor_cb, 1, 0, 1, 3) grid.addWidget(self.proxy_cb, 2, 0, 1, 3) grid.addWidget( HelpButton( _('Proxy settings apply to all connections: with Electrum-AXE servers, but also with third-party services.' )), 2, 4) grid.addWidget(self.proxy_mode, 4, 1) grid.addWidget(self.proxy_host, 4, 2) grid.addWidget(self.proxy_port, 4, 3) grid.addWidget(self.proxy_user, 5, 2) grid.addWidget(self.proxy_password, 5, 3) grid.setRowStretch(7, 1) # Blockchain Tab grid = QGridLayout(blockchain_tab) msg = ' '.join([ _("Electrum-AXE connects to several nodes in order to download block headers and find out the longest blockchain." ), _("This blockchain is used to verify the transactions sent by your transaction server." ) ]) self.status_label = QLabel('') grid.addWidget(QLabel(_('Status') + ':'), 0, 0) grid.addWidget(self.status_label, 0, 1, 1, 3) grid.addWidget(HelpButton(msg), 0, 4) self.server_label = QLabel('') msg = _( "Electrum-AXE sends your wallet addresses to a single server, in order to receive your transaction history." ) grid.addWidget(QLabel(_('Server') + ':'), 1, 0) grid.addWidget(self.server_label, 1, 1, 1, 3) grid.addWidget(HelpButton(msg), 1, 4) self.height_label = QLabel('') msg = _('This is the height of your local copy of the blockchain.') grid.addWidget(QLabel(_('Blockchain') + ':'), 2, 0) grid.addWidget(self.height_label, 2, 1) grid.addWidget(HelpButton(msg), 2, 4) self.split_label = QLabel('') grid.addWidget(self.split_label, 3, 0, 1, 3) self.nodes_list_widget = NodesListWidget(self) grid.addWidget(self.nodes_list_widget, 5, 0, 1, 5) vbox = QVBoxLayout() vbox.addWidget(tabs) self.layout_ = vbox # tor detector self.td = td = TorDetector() td.found_proxy.connect(self.suggest_proxy) td.start() self.fill_in_proxy_settings() 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_axe.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 __init__(self, parent): QTreeWidget.__init__(self) self.parent = parent self.setHeaderLabels([_('Connected node'), _('Height')]) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.create_menu)
def get_toolbar_buttons(self): return QLabel(_("Filter:")), self.change_button, self.used_button
from electrum_axe.i18n import _ fullname = _('Revealer') description = ''.join([ "<br/>", "<b>" + _("Do you have something to hide ?") + "</b>", '<br/>', '<br/>', _("Revealer is a seed phrase back-up solution. It allows you to create a cold, analog, multi-factor backup of your wallet seeds, or of any arbitrary secret." ), '<br/>', '<br/>', _("Using a Revealer is better than writing your seed phrases on paper: a revealer is invulnerable to physical access and allows creation of trustless redundancy." ), '<br/>', '<br/>', _("This plug-in allows you to generate a pdf file of your secret phrase encrypted visually for your physical Revealer. You can print it trustlessly - it can only be decrypted optically with your Revealer." ), '<br/>', '<br/>', _("The plug-in also allows you to generate a digital Revealer file and print it yourself on a transparent overhead foil." ), '<br/>', '<br/>', _("Once activated you can access the plug-in through the icon at the seed dialog." ), '<br/>', '<br/>', _("For more information, visit"), " <a href=\"https://revealer.cc\">https://revealer.cc</a>", '<br/>',
QLineEdit, QRadioButton, QCheckBox, QWidget, QMessageBox, QFileDialog, QSlider, QTabWidget) from electrum_axe.gui.qt.util import (WindowModalDialog, WWLabel, Buttons, CancelButton, OkButton, CloseButton) from electrum_axe.i18n import _ from electrum_axe.plugin import hook from electrum_axe.util import bh2u from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from ..hw_wallet.plugin import only_hook_if_libraries_available from .trezor import (TrezorPlugin, TIM_NEW, TIM_RECOVER, TrezorInitSettings, RECOVERY_TYPE_SCRAMBLED_WORDS, RECOVERY_TYPE_MATRIX) 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 Axe 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 Axe coins if your device is lost or stolen.") PASSPHRASE_NOT_PIN = _( "If you forget a passphrase you will be unable to access any " "Axe coins in the wallet behind it. A passphrase is not a PIN. " "Only change this if you are sure you understand it.") MATRIX_RECOVERY = _( "Enter the recovery words by pressing the buttons according to what "
def setup_dialog(self, window): self.wallet = window.parent().wallet self.update_wallet_name(self.wallet) self.user_input = False self.d = WindowModalDialog(window, "Setup Dialog") self.d.setMinimumWidth(500) self.d.setMinimumHeight(210) self.d.setMaximumHeight(320) self.d.setContentsMargins(11, 11, 1, 1) self.hbox = QHBoxLayout(self.d) vbox = QVBoxLayout() logo = QLabel() self.hbox.addWidget(logo) logo.setPixmap(QPixmap(icon_path('revealer.png'))) logo.setAlignment(Qt.AlignLeft) self.hbox.addSpacing(16) vbox.addWidget( WWLabel( "<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" + _("To encrypt your backup, first we need to load some noise.") + "<br/>")) vbox.addSpacing(7) bcreate = QPushButton(_("Create a new Revealer")) bcreate.setMaximumWidth(181) bcreate.setDefault(True) vbox.addWidget(bcreate, Qt.AlignCenter) self.load_noise = ScanQRTextEdit() self.load_noise.setTabChangesFocus(True) self.load_noise.textChanged.connect(self.on_edit) self.load_noise.setMaximumHeight(33) self.hbox.addLayout(vbox) vbox.addWidget( WWLabel( _("or type an existing revealer code below and click 'next':")) ) vbox.addWidget(self.load_noise) vbox.addSpacing(3) self.next_button = QPushButton(_("Next"), self.d) self.next_button.setEnabled(False) vbox.addLayout(Buttons(self.next_button)) self.next_button.clicked.connect(self.d.close) self.next_button.clicked.connect( partial(self.cypherseed_dialog, window)) vbox.addWidget( QLabel("<b>" + _("Warning") + "</b>: " + _( "Each revealer should be used only once." ) + "<br>" + _( "more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>" ))) def mk_digital(): try: self.make_digital(self.d) except Exception: self.logger.exception('') else: self.cypherseed_dialog(window) bcreate.clicked.connect(mk_digital) return bool(self.d.exec_())