def on_update(self): item = self.currentItem() current_tx, current_i = item.data(0, Qt.UserRole) if item else (None, None) self.clear() wallet = self.parent.wallet pmw = self.parent.pmw key = self.parent.key mypubkey = key.pubkey myaddress = key.address # internal function to be called within loop below def putitem(i, typ, datastr): if to_me: to_str = 'me' elif to_pubkey: to_str = to_pubkey[-3:].hex() else: to_str = 'unk' item = SortableTreeWidgetItem([ '', status_str, 'me' if from_me else from_pubkey[-3:].hex(), to_str, typ, datastr, ]) if status not in self.statusIcons: self.statusIcons[status] = QIcon(":icons/" + TX_ICONS[status]) icon = self.statusIcons[status] item.setIcon(0, icon) item.setData(0, SortableTreeWidgetItem.DataRole, (status, conf)) item.setToolTip( 0, str(conf) + " confirmation" + ("s" if conf != 1 else "")) item.setData(0, Qt.UserRole, (tx_hash, i)) item.setData(2, Qt.UserRole, from_pubkey) item.setData(3, Qt.UserRole, to_pubkey) item.setToolTip(5, '<p>%s</p>' % (escape(datastr), )) self.insertTopLevelItem(0, item) if current_tx == tx_hash and current_i == i: self.setCurrentItem(item) return item for tx_hash, height in wallet.get_address_history(myaddress): info = pmw.messageinfo.get(tx_hash) if not info: continue height, conf, timestamp = wallet.get_tx_height(tx_hash) status, status_str = wallet.get_tx_status(tx_hash, height, conf, timestamp) from_pubkey = info['src'] dest_addr = info['dst'] from_me = (from_pubkey == mypubkey) to_me = (dest_addr == myaddress) if to_me: to_pubkey = mypubkey else: to_pubkey = pmw.known_pubkeys.get(dest_addr) if info['status'] == 'processing': # tx needs to be verified putitem(0, '-', 'verifying') continue messagebytes = info.get('message') if messagebytes is None: putitem(0, '?', '') continue try: osm = openswap.OpenSwapMessage.from_bytes(messagebytes) except Exception as e: try: message = repr(messagebytes.decode('utf8')) except: message = messagebytes.hex() putitem(0, 'raw', "raw: " + message) continue for i, pak in enumerate(osm.packets): if isinstance(pak, openswap.PacketPad): # skip padding continue if isinstance(pak, openswap.PacketOffer) and not from_me: # save incoming offer packets self.incoming_offers[(from_pubkey, pak.offer_info)] = pak try: datastr = pak.to_ui_string() except Exception as e: print(e) datastr = str(pak) if isinstance(pak, openswap.PacketOffer): remain = pak.expire_time - time.time() if remain > 0: expstr = _("%d minutes remain") % (round(remain / 60.)) else: expstr = _("expired") if from_me: fmtstr = _('You offer %s%s to get %s%s (%s)') else: fmtstr = _('They offer %s%s to get %s%s (%s)') datastr = fmtstr % ( format_satoshis_plain_nofloat( pak.offer_info.give_amount), pak.offer_info.give_ticker.decode('utf8'), format_satoshis_plain_nofloat( pak.offer_info.want_amount), pak.offer_info.want_ticker.decode('utf8'), expstr, ) item = putitem(i, 'OS', datastr) item.setData(5, Qt.UserRole, pak)
def notify_timedout(self, i): self.update_table_cb(i, _('Timed out'))
from electroncash.util import print_error, is_verbose, bfh, bh2u, versiontuple try: import hid from btchip.btchipComm import HIDDongleHIDAPI, DongleWait from btchip.btchip import btchip from btchip.btchipUtils import compress_public_key, format_transaction, get_regular_input_script, get_p2sh_input_script from btchip.bitcoinTransaction import bitcoinTransaction from btchip.btchipFirmwareWizard import checkFirmware, updateFirmware from btchip.btchipException import BTChipException BTCHIP = True BTCHIP_DEBUG = is_verbose except ImportError: BTCHIP = False MSG_NEEDS_FW_UPDATE_CASHADDR = _('Firmware version (or "Bitcoin Cash" app) too old for CashAddr support. ') + \ _('Please update at https://www.ledgerwallet.com') MSG_NEEDS_SW_UPDATE_CASHADDR = _('python-btchip is too old for CashAddr support. ') + \ _('Please update to v0.1.27 or greater') BITCOIN_CASH_SUPPORT_HW1 = (1, 0, 4) BITCOIN_CASH_SUPPORT = (1, 1, 8) CASHADDR_SUPPORT = (1, 2, 5) MULTI_OUTPUT_SUPPORT = (1, 1, 4) def test_pin_unlocked(func): """Function decorator to test the Ledger for being unlocked, and if not, raise a human-readable exception. """ def catch_exception(self, *args, **kwargs): try:
def run_and_get_wallet(self): vbox = QVBoxLayout() hbox = QHBoxLayout() hbox.addWidget(QLabel(_('Wallet') + ':')) self.name_e = QLineEdit() hbox.addWidget(self.name_e) button = QPushButton(_('Choose...')) hbox.addWidget(button) vbox.addLayout(hbox) self.msg_label = QLabel('') vbox.addWidget(self.msg_label) hbox2 = QHBoxLayout() self.pw_e = QLineEdit('', self) self.pw_e.setFixedWidth(150) self.pw_e.setEchoMode(2) self.pw_label = QLabel(_('Password') + ':') hbox2.addWidget(self.pw_label) hbox2.addWidget(self.pw_e) hbox2.addStretch() vbox.addLayout(hbox2) self.set_layout(vbox, title=_(f'{PROJECT_NAME} wallet')) wallet_folder = os.path.dirname(self.storage.path) def on_choose(): path, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) if path: self.name_e.setText(path) def on_filename(filename): path = os.path.join(wallet_folder, filename) try: self.storage = WalletStorage(path, manual_upgrades=True) self.next_button.setEnabled(True) except IOError: self.storage = None self.next_button.setEnabled(False) if self.storage: if not self.storage.file_exists(): msg =_("This file does not exist.") + '\n' \ + _("Press 'Next' to create this wallet, or choose another file.") pw = False elif self.storage.file_exists() and self.storage.is_encrypted( ): msg = _("This file is encrypted.") + '\n' + _( 'Enter your password or choose another file.') pw = True else: msg = _("Press 'Next' to open this wallet.") pw = False else: msg = _('Cannot read file') pw = False self.msg_label.setText(msg) if pw: self.pw_label.show() self.pw_e.show() self.pw_e.setFocus() else: self.pw_label.hide() self.pw_e.hide() button.clicked.connect(on_choose) self.name_e.textChanged.connect(on_filename) n = os.path.basename(self.storage.path) self.name_e.setText(n) while True: password = None if self.storage.file_exists() and not self.storage.is_encrypted(): break if self.loop.exec_() != 2: # 2 = next return if not self.storage.file_exists(): break if self.storage.file_exists() and self.storage.is_encrypted(): password = self.pw_e.text() try: self.storage.decrypt(password) break except InvalidPassword as e: QMessageBox.information(None, _('Error'), str(e)) continue except BaseException as e: traceback.print_exc(file=sys.stdout) QMessageBox.information(None, _('Error'), str(e)) return path = self.storage.path if self.storage.requires_split(): self.hide() msg = _( "The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n" "Do you want to split your wallet into multiple files?" ).format(path) if not self.question(msg): return file_list = '\n'.join(self.storage.split_accounts()) msg = _('Your accounts have been moved to' ) + ':\n' + file_list + '\n\n' + _( 'Do you want to delete the old file') + ':\n' + path if self.question(msg): os.remove(path) self.show_warning(_('The file was removed')) return if self.storage.requires_upgrade(): self.hide() msg = _(f"The format of your wallet {path} must be upgraded for " f"{PROJECT_NAME}. This change will not be backward " f"compatible") if not self.question(msg): return self.storage.upgrade() self.wallet = Wallet(self.storage) return self.wallet, password action = self.storage.get_action() if action and action != 'new': self.hide() msg = _("The file '{}' contains an incompletely created wallet.\n" "Do you want to complete its creation now?").format(path) if not self.question(msg): if self.question( _("Do you want to delete '{}'?").format(path)): os.remove(path) self.show_warning(_('The file was removed')) return self.show() if action: # self.wallet is set in run self.run(action) return self.wallet, password self.wallet = Wallet(self.storage) return self.wallet, password
def on_n(n): n_label.setText(_('From %d cosigners') % n) cw.set_n(n) m_edit.setMaximum(n)
def __init__(self, plugin_dialog, main_window, title, plugin_path=None, plugin_metadata=None): WindowModalDialog.__init__(self, parent=main_window.top_level_window(), title=title) self.is_preview = plugin_metadata is None self.main_window = main_window self.plugin_dialog = plugin_dialog self.setMinimumWidth(600) #self.setMaximumWidth(600) vbox = QVBoxLayout() self.setLayout(vbox) groupBox = QGroupBox(_("Plugin Metadata")) self.metadataFormLayout = QFormLayout(groupBox) self.metadataFormLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) self.pluginNameLabel = QLabel() self.metadataFormLayout.addRow(_("Name"), self.pluginNameLabel) self.versionLabel = QLabel() self.metadataFormLayout.addRow(_("Version"), self.versionLabel) self.projectUrlLabel = QLabel() self.projectUrlLabel.setToolTip(_("Click to open in web browser")) self.metadataFormLayout.addRow(_("Project URL"), self.projectUrlLabel) self.descriptionLabel = QLabel() self.descriptionLabel.setWordWrap(True) # Long description labels that wrap should push the form layout's row to grow p = self.descriptionLabel.sizePolicy(); p.setVerticalPolicy(QSizePolicy.MinimumExpanding); self.descriptionLabel.setSizePolicy(p) self.metadataFormLayout.addRow(_("Description"), self.descriptionLabel) self.supportedInterfacesLayout = QVBoxLayout() self.supportedInterfacesLabel = QLabel(_("Integration")) self.supportedInterfacesLabel.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.supportedInterfacesLabel.setToolTip(_("Plugins should support one or more of these interfaces.")) self.metadataFormLayout.addRow(self.supportedInterfacesLabel, self.supportedInterfacesLayout) self.qtInterfaceLabel = QLabel() self.qtInterfaceLabel.setMaximumWidth(20) row = QHBoxLayout() row.addWidget(self.qtInterfaceLabel) row.addWidget(QLabel(_("User interface."))) self.supportedInterfacesLayout.addLayout(row) self.cmdLineInterfaceLabel = QLabel() self.cmdLineInterfaceLabel.setMaximumWidth(20) row = QHBoxLayout() row.addWidget(self.cmdLineInterfaceLabel) row.addWidget(QLabel(_("Command-line."))) self.supportedInterfacesLayout.addLayout(row) self.checksumLabel = QLabel() self.checksumLabel.setToolTip(_("If the official source for this plugin has a checksum for this plugin, ensure that the value shown here is the same.")) self.checksumLabel.setTextInteractionFlags(Qt.TextSelectableByMouse) self.metadataFormLayout.addRow(_("SHA256 Checksum"), self.checksumLabel) vbox.addWidget(groupBox,3) if self.is_preview: confirmLayout = QVBoxLayout() confirmLayout.setAlignment(Qt.AlignHCenter) confirmGroupBox = QGroupBox(_("Risks and Dangers")) liabilityLabel = QLabel(_("I accept responsibility for any harm that comes from installing this plugin, and acknowledge:")) rows = QVBoxLayout() self.liabilityCheckbox1 = QCheckBox(_("The Electron Cash Developers do NOT audit or vet any plugins.")) self.liabilityCheckbox2 = QCheckBox(_("Plugins are risky. They can steal funds or even damage your computer.")) self.liabilityCheckbox3 = QCheckBox(_("I should only install the most reputable plugins trusted by the community.")) confirmLayout.addWidget(liabilityLabel) confirmLayout.addWidget(self.liabilityCheckbox1) confirmLayout.addWidget(self.liabilityCheckbox2) confirmLayout.addWidget(self.liabilityCheckbox3) confirmGroupBox.setLayout(confirmLayout) vbox.addWidget(confirmGroupBox) hbox = QHBoxLayout() vbox.addLayout(hbox) self.installButton = QPushButton("Install") self.cancelButton = QPushButton("Close") self.cancelButton.setDefault(True) if sys.platform == 'darwin': # macOS convention is Cancel-on-left, "Action" on right hbox.addWidget(self.cancelButton) hbox.addStretch(1) hbox.addWidget(self.installButton) else: # non-macOS, go with the Windows convention of Cancel-on-right hbox.addWidget(self.installButton) hbox.addStretch(1) hbox.addWidget(self.cancelButton) self.installButton.clicked.connect(self.on_install) self.cancelButton.clicked.connect(self.close) self.liabilityCheckbox1.clicked.connect(self.on_liability_toggled) self.liabilityCheckbox2.clicked.connect(self.on_liability_toggled) self.liabilityCheckbox3.clicked.connect(self.on_liability_toggled) else: hbox = QHBoxLayout() vbox.addLayout(hbox) self.cancelButton = QPushButton("Close") self.cancelButton.setDefault(True) hbox.addStretch(1) hbox.addWidget(self.cancelButton) self.cancelButton.clicked.connect(self.close) self.pluginNameLabel.setText(_("Unavailable.")) self.projectUrlLabel.setText(_("Unavailable.")) self.versionLabel.setText(_("Unavailable.")) self.descriptionLabel.setText(_("Unavailable.")) self.checksumLabel.setText(_("Unavailable.")) if plugin_path is not None: self.refresh_plugin_from_path(plugin_path) elif plugin_metadata is not None: self.refresh_plugin_from_metadata(plugin_metadata) self.refresh_ui()
def on_update(self): if self.cleaned_up: return item = self.currentItem() current_contact = item.data(0, self.DataRoles.Contact) if item else None selected = self.selectedItems() or [] selected_contacts = set( item.data(0, self.DataRoles.Contact) for item in selected) del item, selected # must not hold a reference to a C++ object that will soon be deleted in self.clear().. self.clear() type_names = defaultdict(lambda: _("Unknown")) type_names.update({ 'openalias': _('OpenAlias'), 'cashacct': _('Cash Account'), 'cashacct_W': _('Cash Account') + ' [' + _('Mine') + ']', 'cashacct_T': _('Cash Account') + ' [' + _('Pend') + ']', 'address': _('Address'), }) type_icons = { 'openalias': self.icon_openalias, 'cashacct': self.icon_cashacct, 'cashacct_W': self.icon_cashacct, 'cashacct_T': self.icon_unverif, 'address': self.icon_contacts, } selected_items, current_item = [], None edited = self._edited_item_cur_sel for contact in self.get_full_contacts( include_pseudo=self.show_my_cashaccts): _type, name, address = contact.type, contact.name, contact.address label_key = address if _type in ('cashacct', 'cashacct_W', 'cashacct_T', 'address'): try: # try and re-parse and re-display the address based on current UI string settings addy = Address.from_string(address) address = addy.to_ui_string() label_key = addy.to_storage_string() del addy except: ''' This may happen because we may not have always enforced this as strictly as we could have in legacy code. Just move on.. ''' label = self.wallet.get_label(label_key) item = QTreeWidgetItem( ["", name, label, address, type_names[_type]]) item.setData(0, self.DataRoles.Contact, contact) item.DataRole = self.DataRoles.Contact if _type in ('cashacct', 'cashacct_W', 'cashacct_T'): ca_info = self.wallet.cashacct.get_verified(name) tt_warn = None if ca_info: if self.wallet.is_mine( ca_info.address) and not self.show_my_cashaccts: # user may have added the contact to "self" manually # but since they asked to not see their own cashaccts, # we must do this to suppress it from being shown regardless continue item.setText(0, ca_info.emoji) tt = _( 'Validated Cash Account: <b><pre>{emoji} {account_string}</pre></b>' ).format( emoji=ca_info.emoji, account_string= f'{ca_info.name}#{ca_info.number}.{ca_info.collision_hash};' ) else: item.setIcon(0, self.icon_unverif) if _type == 'cashacct_T': tt_warn = tt = _( 'Cash Account pending confirmation and/or verification' ) else: tt_warn = tt = _( 'Warning: This Cash Account is not verified') item.setToolTip(0, tt) if tt_warn: item.setToolTip(1, tt_warn) if _type in type_icons: item.setIcon(4, type_icons[_type]) # always give the "Address" field a monospace font even if it's # not strictly an address such as openalias... item.setFont(3, self.monospace_font) self.addTopLevelItem(item) if contact == current_contact or (contact == edited[0] and edited[1]): current_item = item # this key was the current item before and it hasn't gone away if contact in selected_contacts or (contact == edited[0] and edited[2]): selected_items.append( item ) # this key was selected before and it hasn't gone away if selected_items: # sometimes currentItem is set even if nothing actually selected. grr.. # restore current item & selections if current_item: # set the current item. this may also implicitly select it self.setCurrentItem(current_item) for item in selected_items: # restore the previous selection item.setSelected(True) self._edited_item_cur_sel = (None, ) * 3 run_hook('update_contacts_tab', self)
def __init__(self, parent): QTreeWidget.__init__(self) self.parent = parent self.setHeaderLabels(['', _('Host'), _('Port')]) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.create_menu)
def __init__(self, parent, network, config, wizard=False): super().__init__(parent) self.network = network self.config = config self.protocol = None self.tor_proxy = None # tor detector self.td = TorDetector(self) self.td.found_proxy.connect(self.suggest_proxy) self.tabs = tabs = QTabWidget() server_tab = QWidget() weakTd = Weak.ref(self.td) class ProxyTab(QWidget): def showEvent(slf, e): super().showEvent(e) td = weakTd() if e.isAccepted() and td: td.start( ) # starts the tor detector when proxy_tab appears def hideEvent(slf, e): super().hideEvent(e) td = weakTd() if e.isAccepted() and td: td.stop( ) # stops the tor detector when proxy_tab disappears proxy_tab = ProxyTab() blockchain_tab = QWidget() tabs.addTab(blockchain_tab, _('Overview')) tabs.addTab(server_tab, _('Server')) tabs.addTab(proxy_tab, _('Proxy')) if wizard: tabs.setCurrentIndex(1) # 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.ssl_cb = QCheckBox(_('Use SSL')) self.autoconnect_cb = QCheckBox(_('Select server automatically')) self.autoconnect_cb.setEnabled( self.config.is_modifiable('auto_connect')) weakSelf = Weak.ref( self ) # Qt/Python GC hygeine: avoid strong references to self in lambda slots. self.server_host.editingFinished.connect( lambda: weakSelf() and weakSelf().set_server(onion_hack=True)) self.server_port.editingFinished.connect( lambda: weakSelf() and weakSelf().set_server(onion_hack=True)) self.ssl_cb.clicked.connect(self.change_protocol) self.autoconnect_cb.clicked.connect(self.set_server) self.autoconnect_cb.clicked.connect(self.update) msg = ' '.join([ _("If auto-connect is enabled, Electron Cash 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. Electron Cash will warn you if your server is lagging." ) ]) grid.addWidget(self.autoconnect_cb, 0, 0, 1, 3) grid.addWidget(HelpButton(msg), 0, 4) self.preferred_only_cb = QCheckBox( _("Connect only to preferred servers")) self.preferred_only_cb.setEnabled( self.config.is_modifiable('whitelist_servers_only')) self.preferred_only_cb.setToolTip( _("If enabled, restricts Electron Cash to connecting to servers only marked as 'preferred'." )) self.preferred_only_cb.clicked.connect( self.set_whitelisted_only ) # re-set the config key and notify network.py msg = '\n\n'.join([ _("If 'Connect only to preferred servers' is enabled, Electron Cash will only connect to servers marked as 'preferred' servers ({})." ).format(ServerFlag.Symbol[ServerFlag.Preferred]), _("This feature was added in response to the potential for a malicious actor to deny service via launching many servers (aka a sybil attack)." ), _("If unsure, most of the time it's safe to leave this option disabled. However leaving it enabled is safer (if a little bit discouraging to new server operators wanting to populate their servers)." ) ]) grid.addWidget(self.preferred_only_cb, 1, 0, 1, 3) grid.addWidget(HelpButton(msg), 1, 4) grid.addWidget(self.ssl_cb, 2, 0, 1, 3) self.ssl_help = HelpButton( _('SSL is used to authenticate and encrypt your connections with the blockchain servers.' ) + "\n\n" + _('Due to potential security risks, you may only disable SSL when using a Tor Proxy.' )) grid.addWidget(self.ssl_help, 2, 4) grid.addWidget(QLabel(_('Server') + ':'), 3, 0) grid.addWidget(self.server_host, 3, 1, 1, 2) grid.addWidget(self.server_port, 3, 3) self.server_list_label = label = QLabel( '') # will get set by self.update() grid.addWidget(label, 4, 0, 1, 5) self.servers_list = ServerListWidget(self) grid.addWidget(self.servers_list, 5, 0, 1, 5) self.legend_label = label = WWLabel( '') # will get populated with the legend by self.update() label.setTextInteractionFlags(label.textInteractionFlags() & ( ~Qt.TextSelectableByMouse)) # disable text selection by mouse here self.legend_label.linkActivated.connect(self.on_view_blacklist) grid.addWidget(label, 6, 0, 1, 4) msg = ' '.join([ _("Preferred servers ({}) are servers you have designated as reliable and/or trustworthy." ).format(ServerFlag.Symbol[ServerFlag.Preferred]), _("Initially, the preferred list is the hard-coded list of known-good servers vetted by the Electron Cash developers." ), _("You can add or remove any server from this list and optionally elect to only connect to preferred servers." ), "\n\n" + _("Banned servers ({}) are servers deemed unreliable and/or untrustworthy, and so they will never be connected-to by Electron Cash." ).format(ServerFlag.Symbol[ServerFlag.Banned]) ]) grid.addWidget(HelpButton(msg), 6, 4) # 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.svg")) self.tor_cb.setEnabled(False) 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 Electron Cash 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([ _("DeLight 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('') self.status_label.setTextInteractionFlags( self.status_label.textInteractionFlags() | Qt.TextSelectableByMouse) 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('') self.server_label.setTextInteractionFlags( self.server_label.textInteractionFlags() | Qt.TextSelectableByMouse) msg = _( "DeLight 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('') self.height_label.setTextInteractionFlags( self.height_label.textInteractionFlags() | Qt.TextSelectableByMouse) 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('') self.split_label.setTextInteractionFlags( self.split_label.textInteractionFlags() | Qt.TextSelectableByMouse) 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 self.fill_in_proxy_settings() self.update()
def __init__(self, window): QWidget.__init__(self) self.window = window self.timer = QtCore.QTimer() self.update_inputs_timer = QtCore.QTimer() self.waiting_timeout = 180 self.timer.timeout.connect(self.tick) self.update_inputs_timer.timeout.connect(self.update_inputs) self.update_inputs_timer.start(15000) self.coinshuffle_fee_constant = 1000 # This is for debug # self.coinshuffle_fee_constant = 1000 # self.coinshuffle_amounts = [1e7, 1e6] # Use this in test mode self.coinshuffle_amounts = [1e5, 1e6, 1e7] self.shuffle_grid = QGridLayout() self.shuffle_grid.setSpacing(8) self.shuffle_grid.setColumnStretch(3, 1) self.coinshuffle_servers = ServersList() self.coinshuffle_inputs = InputAdressWidget( decimal_point=self.window.get_decimal_point) self.coinshuffle_changes = ChangeAdressWidget() self.coinshuffle_fresh_changes = QCheckBox( _('Show only fresh change addresses')) self.coinshuffle_outputs = OutputAdressWidget() self.coinshuffle_amount_radio = AmountSelect( self.coinshuffle_amounts, decimal_point=self.window.get_decimal_point) self.coinshuffle_fee = QLabel( _( self.window.format_amount_and_units( self.coinshuffle_fee_constant))) self.coinshuffle_text_output = ConsoleOutput() self.coinshuffle_timer_output = QLabel() self.coinshuffle_inputs.currentIndexChanged.connect( self.check_sufficient_ammount) self.coinshuffle_amount_radio.button_group.buttonClicked.connect( self.check_sufficient_ammount) self.coinshuffle_fresh_changes.stateChanged.connect( lambda: self.coinshuffle_changes.update( self.window.wallet, fresh_only=self.coinshuffle_fresh_changes.isChecked())) self.coinshuffle_start_button = EnterButton( _("Shuffle"), lambda: self.start_coinshuffle_protocol()) self.coinshuffle_cancel_button = EnterButton( _("Cancel"), lambda: self.cancel_coinshuffle_protocol()) self.coinshuffle_start_button.setEnabled(False) self.coinshuffle_cancel_button.setEnabled(False) self.shuffle_grid.addWidget(QLabel(_('Shuffle server')), 1, 0) self.shuffle_grid.addWidget(QLabel(_('Shuffle input address')), 2, 0) self.shuffle_grid.addWidget(QLabel(_('Shuffle change address')), 3, 0) self.shuffle_grid.addWidget(QLabel(_('Shuffle output address')), 5, 0) self.shuffle_grid.addWidget(QLabel(_('Amount')), 6, 0) self.shuffle_grid.addWidget(QLabel(_('Fee')), 7, 0) self.shuffle_grid.addWidget(self.coinshuffle_servers, 1, 1, 1, -1) self.shuffle_grid.addWidget(self.coinshuffle_fresh_changes, 4, 1) self.shuffle_grid.addWidget(self.coinshuffle_inputs, 2, 1, 1, -1) self.shuffle_grid.addWidget(self.coinshuffle_changes, 3, 1, 1, -1) self.shuffle_grid.addWidget(self.coinshuffle_outputs, 5, 1, 1, -1) self.shuffle_grid.addWidget(self.coinshuffle_amount_radio, 6, 1) self.shuffle_grid.addWidget(self.coinshuffle_fee, 7, 1) self.shuffle_grid.addWidget(self.coinshuffle_start_button, 8, 0) self.shuffle_grid.addWidget(self.coinshuffle_cancel_button, 8, 1) self.shuffle_grid.addWidget(self.coinshuffle_timer_output, 8, 2) self.shuffle_grid.addWidget(self.coinshuffle_text_output, 9, 0, 1, -1) vbox0 = QVBoxLayout() vbox0.addLayout(self.shuffle_grid) hbox = QHBoxLayout() hbox.addLayout(vbox0) vbox = QVBoxLayout(self) vbox.addLayout(hbox) vbox.addStretch(1)
from electroncash.i18n import _ fullname = _('Audio MODEM') description = _('Provides support for air-gapped transaction signing.') requires = [('amodem', 'http://github.com/romanz/amodem/')] available_for = ['qt']
def description(self): return _("Configure CashShuffle Protocol")
def __init__(self, main_window, pmw): # top level window QDialog.__init__(self, parent=None) self.pmw = pmw self.key = pmw.key self.address = self.key.address pubkey = self.key.pubkey.hex() self.wallet = pmw.wallet self.network = pmw.wallet.network self.main_window = main_window self.config = main_window.config self.app = main_window.app self.setWindowTitle(_("OpenSwap Private Messages")) self.setMinimumWidth(700) vbox = QVBoxLayout() self.setLayout(vbox) vbox.addWidget( QLabel(_("Address") + ': ' + self.address.to_ui_string())) vbox.addWidget(QLabel(_("Public key") + ':')) pubkey_e = ButtonsLineEdit(pubkey) pubkey_e.setReadOnly(True) pubkey_e.addCopyButton(self.app) vbox.addWidget(pubkey_e) vbox.addWidget(QLabel(_("History"))) self.hw = MyHistoryList(self) vbox.addWidget(self.hw) hbox = QHBoxLayout() b = QPushButton(_("Write")) b.clicked.connect(lambda: self.write_message()) hbox.addWidget(b) b = QPushButton(_("Offer")) b.clicked.connect(lambda: self.make_offer()) hbox.addWidget(b) b = QPushButton(_("View Public Offers")) b.clicked.connect( lambda: prompt_dialog(self.main_window, 0, 1, pmw=self.pmw)) hbox.addWidget(b) hbox.addStretch(1) hbox.addWidget(CloseButton(self)) vbox.addLayout(hbox) self.show() self.gotDecryptSig.connect(self.hw.got_decrypted) def on_success(result): pmw.callbacks_decrypted.append(self.gotDecryptSig.emit) self.hw.update() def on_error(einfo): import traceback traceback.print_exception(*einfo) d = WaitingDialog(self, _('Opening...'), pmw.start, on_success, on_error)
def create_menu(self, position): self.selectedIndexes() item = self.currentItem() if not item: return tx_hash, i = item.data(0, Qt.UserRole) if not tx_hash: return key = self.parent.key mypubkey = key.pubkey from_pubkey = item.data(2, Qt.UserRole) to_pubkey = item.data(3, Qt.UserRole) from_me = (from_pubkey == mypubkey) to_me = (to_pubkey == mypubkey) if from_me: other_pubkey = to_pubkey else: other_pubkey = from_pubkey packet = item.data(5, Qt.UserRole) column = self.currentColumn() if column == 0: column_title = "ID" column_data = tx_hash elif column == 2: column_title = _("sender pubkey") column_data = from_pubkey.hex() elif column == 3: column_title = _("recipient pubkey") if to_pubkey: column_data = to_pubkey.hex() else: column_data = "" else: column_title = self.headerItem().text(column) column_data = item.text(column) menu = QMenu() if isinstance(packet, openswap.PacketOffer): if from_me: menu.addAction( _("View/Re-offer"), lambda: self.parent.view_offer_as_sender( to_pubkey, packet)) else: menu.addAction( _("View/Counter-offer/Accept"), lambda: self.parent.view_offer_as_recipient( from_pubkey, packet)) if isinstance(packet, openswap.PacketAccept): if from_me: offerpacket = self.incoming_offers.get( (to_pubkey, packet.offer_info)) else: offerpacket = None act = menu.addAction( _("Atomic Swap"), lambda: self.parent.start_swap( from_me, other_pubkey, offerpacket, packet)) if from_me and not offerpacket: # If we have accepted other party's offer but we don't have their # offer packet, then we can't do a swap as the keys are not # available! act.setEnabled(False) if to_me: menu.addAction( _("Reply raw message"), lambda: self.parent.write_message(from_pubkey.hex())) elif to_pubkey: menu.addAction(_("Write another raw message"), lambda: self.parent.write_message(to_pubkey.hex())) menu.addAction( _("Copy {}").format(column_title), lambda: self.parent.main_window. app.clipboard().setText(column_data)) def showtx(): tx = self.wallet.transactions.get(tx_hash) self.parent.main_window.show_transaction(tx) menu.addAction(_("View Tx"), showtx) menu.exec_(self.viewport().mapToGlobal(position))
def on_uninstall_plugin(self): package_name = self.pluginsList.get_selected_key() if self.question(_("Are you sure you want to uninstall the selected plugin?")): plugin_manager = self.main_window.gui_object.plugins plugin_manager.uninstall_external_plugin(package_name) self.refresh_ui()
def update(self): host, port, protocol, proxy_config, auto_connect = self.network.get_parameters( ) preferred_only = self.network.is_whitelist_only() if not self.server_host.hasFocus() and not self.server_port.hasFocus(): self.server_host.setText(host) self.server_port.setText(port) self.ssl_cb.setChecked(protocol == 's') ssl_disable = self.ssl_cb.isChecked() and not self.tor_cb.isChecked( ) and not host.lower().endswith('.onion') for w in [self.ssl_cb]: #, self.ssl_help]: w.setDisabled(ssl_disable) self.autoconnect_cb.setChecked(auto_connect) self.preferred_only_cb.setChecked(preferred_only) host = self.network.interface.host if self.network.interface else _( 'None') self.server_label.setText(host) self.set_protocol(protocol) self.servers = self.network.get_servers() def protocol_suffix(): if protocol == 't': return ' (non-SSL)' elif protocol == 's': return ' [SSL]' return '' server_list_txt = (_('Server peers') if self.network.is_connected() else _('Servers')) + " ({})".format( len(self.servers)) server_list_txt += protocol_suffix() self.server_list_label.setText(server_list_txt) if self.network.blacklisted_servers: bl_srv_ct_str = ' ({}) <a href="ViewBanList">{}</a>'.format( len(self.network.blacklisted_servers), _("View ban list...")) else: bl_srv_ct_str = " (0)<i> </i>" # ensure rich text servers_whitelisted = set(get_eligible_servers( self.servers, protocol)).intersection( self.network.whitelisted_servers ) - self.network.blacklisted_servers self.legend_label.setText(ServerFlag.Symbol[ServerFlag.Preferred] + "=" + _("Preferred") + " ({})".format(len(servers_whitelisted)) + " " + ServerFlag.Symbol[ServerFlag.Banned] + "=" + _("Banned") + bl_srv_ct_str) self.servers_list.update(self.network, self.servers, self.protocol, self.tor_cb.isChecked()) self.enable_set_server() height_str = "%d " % (self.network.get_local_height()) + _('blocks') self.height_label.setText(height_str) n = len(self.network.get_interfaces()) status = _("Connected to %d nodes.") % n if n else _("Not connected") if n: status += protocol_suffix() self.status_label.setText(status) chains = self.network.get_blockchains() if len(chains) > 1: chain = self.network.blockchain() checkpoint = chain.get_base_height() name = chain.get_name() msg = _('Chain split detected at block %d') % checkpoint + '\n' msg += (_('You are following branch') if auto_connect else _('Your server is on branch')) + ' ' + name msg += ' (%d %s)' % (chain.get_branch_size(), _('blocks')) else: msg = '' self.split_label.setText(msg) self.nodes_list_widget.update(self.network)
def show_installed_plugin_about_dialog(self, package_name): plugin_manager = self.main_window.gui_object.plugins metadata = plugin_manager.external_plugin_metadata.get(package_name, None) if metadata is None: return self.pluginAboutDialog = d = ExternalPluginsPreviewDialog(self, self.main_window, _("Plugin Details"), plugin_metadata=metadata) d.exec_()
def __init__(self, main_window, exctype, value, tb): self.exc_args = (exctype, value, tb) self.main_window = main_window QWidget.__init__(self) self.setWindowTitle('Electron Cash - ' + _('An Error Occurred')) self.setMinimumSize(600, 300) main_box = QVBoxLayout() heading = QLabel('<h2>' + _('Sorry!') + '</h2>') main_box.addWidget(heading) main_box.addWidget( QLabel(_('Something went wrong running Electron Cash.'))) main_box.addWidget( QLabel( _('To help us diagnose and fix the problem, you can send us a bug report that contains useful debug ' 'information:'))) collapse_info = QPushButton(_("Show report contents")) collapse_info.clicked.connect(lambda: QMessageBox.about( self, "Report contents", self.get_report_string())) main_box.addWidget(collapse_info) label = QLabel( _("Please briefly describe what led to the error (optional):") + "<br/>" + "<i>" + _("Feel free to add your email address if you are willing to provide further detail, but note that it will appear in the relevant github issue." ) + "</i>") label.setTextFormat(QtCore.Qt.RichText) main_box.addWidget(label) self.description_textfield = QTextEdit() self.description_textfield.setFixedHeight(50) main_box.addWidget(self.description_textfield) main_box.addWidget(QLabel(_("Do you want to send this report?"))) buttons = QHBoxLayout() report_button = QPushButton(_('Send Bug Report')) report_button.clicked.connect(self.send_report) report_button.setIcon(QIcon(":icons/tab_send.png")) buttons.addWidget(report_button) never_button = QPushButton(_('Never')) never_button.clicked.connect(self.show_never) buttons.addWidget(never_button) close_button = QPushButton(_('Not Now')) close_button.clicked.connect(self.close) buttons.addWidget(close_button) main_box.addLayout(buttons) self.setLayout(main_box) self.show()
def create_menu(self, position): menu = QMenu() selected = self.selectedItems() i2c = self._i2c ca_unverified = self._get_ca_unverified(include_temp=False) if selected: names = [item.text(1) for item in selected] keys = [i2c(item) for item in selected] payable_keys = [k for k in keys if k.type != 'cashacct_T'] deletable_keys = [k for k in keys if k.type in contact_types] needs_verif_keys = [k for k in keys if k in ca_unverified] column = self.currentColumn() column_title = self.headerItem().text(column) column_data = '\n'.join([item.text(column) for item in selected]) item = self.currentItem() typ = i2c(item).type if item else 'unknown' ca_info = None if item and typ in ('cashacct', 'cashacct_W'): ca_info = self.wallet.cashacct.get_verified(i2c(item).name) if column == 1 and len(selected) == 1: # hack .. for Cash Accounts just say "Copy Cash Account" column_title = _('Cash Account') if ca_info: column_data = self.wallet.cashacct.fmt_info(ca_info, emoji=True) if len(selected) > 1: column_title += f" ({len(selected)})" menu.addAction( _("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) if item and column in self.editable_columns and self.on_permit_edit( item, column): key = item.data(0, self.DataRoles.Contact) # this key & find_item business is so we don't hold a reference # to the ephemeral item, which may be deleted while the # context menu is up. Accessing the item after on_update runs # means the item is deleted and you get a C++ object deleted # runtime error. menu.addAction( _("Edit {}").format(column_title), lambda: self._on_edit_item(key, column)) a = menu.addAction( _("Pay to"), lambda: self.parent.payto_contacts(payable_keys)) if needs_verif_keys or not payable_keys: a.setDisabled(True) a = menu.addAction( _("Delete"), lambda: self.parent.delete_contacts(deletable_keys)) if not deletable_keys: a.setDisabled(True) URLs = [ web.BE_URL(self.config, 'addr', Address.from_string(key.address)) for key in keys if Address.is_valid(key.address) ] a = menu.addAction(_("View on block explorer"), lambda: [URL and webopen(URL) for URL in URLs]) if not any(URLs): a.setDisabled(True) if ca_info: menu.addAction( _("View registration tx..."), lambda: self.parent.do_process_from_txid( txid=ca_info.txid, tx_desc=self.wallet.get_label(ca_info.txid))) if typ in ('cashacct_W', 'cashacct'): _contact_d = i2c(item) menu.addAction( _("Details..."), lambda: cashacctqt.cash_account_detail_dialog( self.parent, _contact_d.name)) menu.addSeparator() menu.addAction(self.icon_cashacct, _("Add Contact") + " - " + _("Cash Account"), self.new_cash_account_contact_dialog) menu.addAction(self.icon_contacts, _("Add Contact") + " - " + _("Address"), self.parent.new_contact_dialog) menu.addSeparator() menu.addAction(self.icon_cashacct, _("Register Cash Account..."), self.parent.register_new_cash_account) menu.addSeparator() menu.addAction( QIcon(":icons/import.svg" if not ColorScheme.dark_scheme else ":icons/import_dark_theme.svg"), _("Import file"), self.import_contacts) if not self.parent.contacts.empty: menu.addAction( QIcon(":icons/save.svg" if not ColorScheme.dark_scheme else ":icons/save_dark_theme.svg"), _("Export file"), self.export_contacts) menu.addSeparator() a = menu.addAction(_("Show My Cash Accounts"), self.toggle_show_my_cashaccts) a.setCheckable(True) a.setChecked(self.show_my_cashaccts) if ca_unverified: def kick_off_verify(): bnums = set() for contact in ca_unverified: tup = self.wallet.cashacct.parse_string(contact.name) if not tup: continue bnums.add(tup[1]) # number ret = cashacctqt.verify_multiple_blocks( bnums, self.parent, self.wallet) if ret is None: # user cancel return verified = ca_unverified - self._get_ca_unverified() if not verified: self.parent.show_error( _("Cash Account verification failure")) menu.addSeparator() num = len(ca_unverified) a = menu.addAction( self.icon_unverif, ngettext("Verify {count} Cash Account", "Verify {count} Cash Accounts", num).format(count=num), kick_off_verify) if not self.wallet.network: a.setDisabled(True) run_hook('create_contact_menu', menu, selected) menu.exec_(self.viewport().mapToGlobal(position))
def decrypt_message(self, sequence, message, password): raise RuntimeError( _('Encryption and decryption are not implemented by {}').format( self.device))
from electroncash.constants import PROJECT_NAME from electroncash import keystore from electroncash.wallet import Standard_Wallet from .seed_dialog import SeedLayout, KeysLayout from .network_dialog import NetworkChoiceLayout from .util import * from .password_dialog import PasswordLayout, PW_NEW from .bip38_importer import Bip38Importer class GoBack(Exception): pass MSG_GENERATING_WAIT = _( f"{PROJECT_NAME} is generating your addresses, please wait...") MSG_ENTER_ANYTHING = _("Please enter a seed phrase, a master key, a list of " "Bitcoin addresses, or a list of private keys") MSG_ENTER_SEED_OR_MPK = _( "Please enter a seed phrase or a master key (xpub or xprv):") MSG_COSIGNER = _("Please enter the master public key of cosigner #{}:") MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\ + _("Leave this field empty if you want to disable encryption.") MSG_RESTORE_PASSPHRASE = \ _("Please enter your seed derivation passphrase. " "Note: this is NOT your encryption password. " "Leave this field empty if you did not use one or are unsure.") class CosignWidget(QWidget): size = 120
def decrypt_message(self, pubkey, message, password): raise RuntimeError( _('Encryption and decryption are currently not supported for %s') % self.device)
def on_m(m): m_label.setText(_('Require %d signatures') % m) cw.set_m(m)
def sign_transaction(self, tx, password): if tx.is_complete(): return client = self.get_client() self.signing = True inputs = [] inputsPaths = [] pubKeys = [] chipInputs = [] redeemScripts = [] signatures = [] preparedTrustedInputs = [] changePath = "" changeAmount = None output = None outputAmount = None p2shTransaction = 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 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) inputs.append([ txin['prev_tx'].raw, txin['prevout_n'], redeemScript, txin['prevout_hash'], signingPos, txin.get('sequence', 0xffffffff - 1) ]) 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 txout in tx.outputs(): output_type, addr, amount = txout txOutput += int_to_hex(amount, 8) script = tx.pay_script(addr) txOutput += var_int(len(script) // 2) txOutput += script txOutput = bfh(txOutput) # Recognize outputs - only one output and one change is authorized if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if (info is not None) and (len(tx.outputs()) != 1): index, xpubs, m = info changePath = self.get_derivation()[2:] + "/%d/%d" % index changeAmount = amount else: output = address outputAmount = amount 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) txtmp = bitcoinTransaction(bfh(utxo[0])) tmp = bfh(utxo[3])[::-1] tmp += bfh(int_to_hex(utxo[1], 4)) tmp += txtmp.outputs[utxo[1]].amount chipInputs.append({ 'value': tmp, 'witness': True, 'sequence': sequence }) redeemScripts.append(bfh(utxo[2])) # Sign all inputs inputIndex = 0 rawTx = tx.serialize() self.get_client().enableAlternate2fa(False) self.get_client().startUntrustedTransaction( True, inputIndex, chipInputs, redeemScripts[inputIndex]) outputData = self.get_client().finalizeInputFull(txOutput) outputData['outputData'] = txOutput transactionOutput = outputData['outputData'] 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() if pin != 'paired': self.handler.show_message( _("Confirmed. Signing Transaction...")) while inputIndex < len(inputs): singleInput = [chipInputs[inputIndex]] self.get_client().startUntrustedTransaction( False, 0, singleInput, redeemScripts[inputIndex]) inputSignature = self.get_client().untrustedHashSign( inputsPaths[inputIndex], pin, lockTime=tx.locktime, sighashType=tx.nHashType()) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 except UserWarning: self.handler.show_error(_('Cancelled by user')) return except BaseException as e: traceback.print_exc(file=sys.stdout) self.give_error(e, True) finally: self.handler.finished() for i, txin in enumerate(tx.inputs()): signingPos = inputs[i][4] txin['signatures'][signingPos] = bh2u(signatures[i]) tx.raw = tx.serialize() self.signing = False
def notify_offline(self): for i, p in enumerate(self.DERIVATION_PATHS): self.update_table_cb(i, _('Offline'))
def show_contact_options_actionsheet(contact: ContactsEntry, vc: ObjCInstance, view: ObjCInstance, navBackOnDelete=False, onEdit=None) -> None: #print ("On Options But") try: parent = gui.ElectrumGui.gui def on_block_explorer() -> None: parent.view_on_block_explorer(contact.address, 'addr') def on_pay_to() -> None: pay_to([contact.address_str]) def on_cpy() -> None: parent.copy_to_clipboard(contact.address_str, 'Address') print("copied to clipboard =", contact.address_str) def on_edit() -> None: show_new_edit_contact(contact, vc, onEdit=onEdit) def on_delete() -> None: def doDelete() -> None: if delete_contact(contact): _Updated() if navBackOnDelete and vc.navigationController: vc.navigationController.popViewControllerAnimated_(True) #utils.show_notification(message = _("Contact deleted.")) parent.question( title=_("Confirm Delete"), message=_("Are you sure you wish to delete this contact?"), onOk=doDelete, vc=vc, destructive=True, okButTitle=_('Delete')) actions = [ [_('Cancel')], [_("Copy Address"), on_cpy], [_("Edit Contact"), on_edit], [_("Pay to"), on_pay_to], [_("View on block explorer"), on_block_explorer], [_("Delete"), on_delete], ] if parent.wallet.is_watching_only(): actions.pop(3) if isinstance(vc, ContactDetailVC): actions.insert(2, [_('Share/Save QR...'), lambda: vc.onQRImgTap()]) utils.show_alert( vc=vc, title=contact.name, #_("Options"), message=contact.address_str, actions=actions, cancel=_('Cancel'), destructive=_('Delete'), style=UIAlertControllerStyleActionSheet, ipadAnchor=view.convertRect_toView_(view.bounds, vc.view) if isinstance(view, UIView) else view) #print ("address =", entry.address_str) except: utils.NSLog( "*** WARNING: Exception during contacts.py 'show_contact_options_actionsheet': %s", str(sys.exc_info()[1]))
def set_scan_progress(self, n): self.label.setText( _('Scanned {}/{}').format( n, len(DerivationPathScanner.DERIVATION_PATHS)))
import hashlib import urllib.parse import sys from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * from electroncash.i18n import _ from electroncash.plugins import ExternalPluginCodes, run_hook from .util import MyTreeWidget, MessageBoxMixin, WindowModalDialog, Buttons, CloseButton INSTALL_ERROR_MESSAGES = { ExternalPluginCodes.MISSING_MANIFEST: _("The plugin archive you selected is missing a manifest. It was therefore not possible to install it."), ExternalPluginCodes.NAME_ALREADY_IN_USE: _("There is already a plugin installed using the internal package name of the plugin you selected. It was therefore not possible to install it."), ExternalPluginCodes.UNABLE_TO_COPY_FILE: _("It was not possible to copy the plugin archive into Electron Cash's plugin storage location. It was therefore not possible to install it."), ExternalPluginCodes.INSTALLED_BUT_FAILED_LOAD: _("The plugin is installed, but in the process of enabling and loading it, an error occurred. Restart Electron Cash and try again, or uninstall it and report it to it's developers."), ExternalPluginCodes.INCOMPATIBLE_VERSION: _("The plugin is targeted at a later version of ViLight."), ExternalPluginCodes.INCOMPATIBLE_ZIP_FORMAT: _("The plugin archive is not recognized as a valid Zip file."), ExternalPluginCodes.INVALID_MANIFEST_JSON: _("The plugin manifest is not recognized as valid JSON."), ExternalPluginCodes.INVALID_MAMIFEST_DISPLAY_NAME: _("The plugin manifest lacks a valid display name."), ExternalPluginCodes.INVALID_MAMIFEST_DESCRIPTION: _("The plugin manifest lacks a valid description."), ExternalPluginCodes.INVALID_MAMIFEST_VERSION: _("The plugin manifest lacks a valid version."), ExternalPluginCodes.INVALID_MAMIFEST_MINIMUM_EC_VERSION: _("The plugin manifest lacks a valid minimum Electron Cash version."), ExternalPluginCodes.INVALID_MAMIFEST_PACKAGE_NAME: _("The plugin manifest lacks a valid package name."), } class ExternalPluginsPreviewDialog(WindowModalDialog):
def perform_hw1_preflight(self): try: firmwareInfo = self.dongleObject.getFirmwareVersion() firmwareVersion = versiontuple(firmwareInfo['version']) self.bitcoinCashSupported = firmwareVersion >= BITCOIN_CASH_SUPPORT or \ self.is_hw1() and firmwareVersion >= BITCOIN_CASH_SUPPORT_HW1 self.cashaddrFWSupported = firmwareVersion >= CASHADDR_SUPPORT self.multiOutputSupported = firmwareVersion >= MULTI_OUTPUT_SUPPORT if not checkFirmware( firmwareInfo) or not self.supports_bitcoin_cash(): self.dongleObject.dongle.close() raise Exception( _("{} firmware version too old. Please update at https://www.ledgerwallet.com" ).format(self.device)) try: self.dongleObject.getOperationMode() except BTChipException as e: if (e.sw == 0x6985): self.dongleObject.dongle.close() self.handler.get_setup() # Acquire the new client on the next run else: raise e if self.has_detached_pin_support( self.dongleObject) and not self.is_pin_validated( self.dongleObject) and (self.handler is not None): remaining_attempts = self.dongleObject.getVerifyPinRemainingAttempts( ) if remaining_attempts != 1: msg = _( 'Enter your {} PIN - remaining attempts: {}').format( self.device, remaining_attempts) else: msg = _( 'Enter your {} PIN - WARNING: LAST ATTEMPT. If the PIN is not correct, the {} will be wiped.' ).format(self.device, self.device) confirmed, p, pin = self.password_dialog(msg) if not confirmed: raise Exception( _('Aborted by user - please unplug the {} and plug it again before retrying' ).format(self.device)) pin = pin.encode() self.dongleObject.verifyPin(pin) gwpkArgSpecs = inspect.getfullargspec( self.dongleObject.getWalletPublicKey) self.cashaddrSWSupported = 'cashAddr' in gwpkArgSpecs.args except BTChipException as e: if (e.sw == 0x6faa): raise Exception( _('{} is temporarily locked - please unplug it and replug it again' ).format(self.device)) if ((e.sw & 0xFFF0) == 0x63c0): raise Exception( _('Invalid PIN - please unplug the {} and plug it again before retrying' ).format(self.device)) if e.sw == 0x6f00 and e.message == 'Invalid channel': # based on docs 0x6f00 might be a more general error, hence we also compare message to be sure raise Exception( _('Invalid channel.') + '\n' + _('Please make sure that \'Browser support\' is disabled on your {}.' ).format(self.device)) raise e
def _ca_set_item_tooltip(self, item, ca_info): minimal_chash = getattr(ca_info, 'minimal_chash', None) info_str = self.wallet.cashacct.fmt_info(ca_info, minimal_chash) item.setToolTip( 0, "<i>" + _("Cash Account:") + "</i><p> <b>" + f"{info_str}</b>")