class PasswordDialog(AbstractPasswordDialog): enter_pw_message = _('Enter your password') enter_new_pw_message = _('Enter new password') confirm_new_pw_message = _('Confirm new password') wrong_password_message = _('Wrong password') allow_disable = False def __init__(self, app, **kwargs): AbstractPasswordDialog.__init__(self, app, **kwargs) self.hide_wallet_label = app._use_single_password def clear_password(self): self.ids.textinput_generic_password.text = '' def on_password(self, pw: str): # if not self.require_password: self.success = True self.message = _('Please wait...') self.dismiss() return # if setting new generic password, enforce min length if self.level > 0: if len(pw) < 6: self.app.show_error( _('Password is too short (min {} characters)').format(6)) return # don't enforce minimum length on existing self.do_check(pw)
def new_request(self, lightning): amount = self.amount amount = self.app.get_amount(amount) if amount else 0 message = self.message lnworker = self.app.wallet.lnworker try: if lightning: if lnworker: key = lnworker.add_request(amount, message, self.expiry()) else: self.app.show_error(_("Lightning payments are not available for this wallet")) return else: addr = self.address or self.app.wallet.get_unused_address() if not addr: if not self.app.wallet.is_deterministic(): addr = self.app.wallet.get_receiving_address() else: self.app.show_info(_('No address available. Please remove some of your pending requests.')) return self.address = addr req = self.app.wallet.make_payment_request(addr, amount, message, self.expiry()) self.app.wallet.add_payment_request(req) key = addr except InvoiceError as e: self.app.show_error(_('Error creating payment request') + ':\n' + str(e)) return self.clear() self.update() self.app.show_request(lightning, key)
def init_storage_from_path(self, path): self.storage = WalletStorage(path) self.basename = self.storage.basename() if not self.storage.file_exists(): self.require_password = False self.message = _('Press Next to create') elif self.storage.is_encrypted(): if not self.storage.is_encrypted_with_user_pw(): raise Exception( "Kivy GUI does not support this type of encrypted wallet files." ) self.pw_check = self.storage.check_password if self.app.password and self.check_password(self.app.password): self.pw = self.app.password # must be set so that it is returned in callback self.require_password = False self.message = _('Press Next to open') else: self.require_password = True self.message = self.enter_pw_message else: # it is a bit wasteful load the wallet here and load it again in main_window, # but that is fine, because we are progressively enforcing storage encryption. db = WalletDB(self.storage.read(), manual_upgrades=False) wallet = Wallet(db, self.storage, config=self.app.electrumsys_config) self.require_password = wallet.has_password() self.pw_check = wallet.check_password self.message = self.enter_pw_message if self.require_password else _( 'Wallet not encrypted')
def do_open_channel(self, funding_tx, conn_str, password): # read funding_sat from tx; converts '!' to int value funding_sat = funding_tx.output_value_for_address(ln_dummy_address()) lnworker = self.app.wallet.lnworker try: chan, funding_tx = lnworker.open_channel( connect_str=conn_str, funding_tx=funding_tx, funding_sat=funding_sat, push_amt_sat=0, password=password) except Exception as e: self.app.logger.exception("Problem opening channel") self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) return # TODO: it would be nice to show this before broadcasting if chan.has_onchain_backup(): self.maybe_show_funding_tx(chan, funding_tx) else: title = _('Save backup') help_text = _(messages.MSG_CREATED_NON_RECOVERABLE_CHANNEL) data = lnworker.export_channel_backup(chan.channel_id) popup = QRDialog( title, data, show_text=False, text_for_clipboard=data, help_text=help_text, close_button_text=_('OK'), on_close=lambda: self.maybe_show_funding_tx(chan, funding_tx)) popup.open()
def do_pay_invoice(self, invoice): if invoice.is_lightning(): if self.app.wallet.lnworker: amount_sat = invoice.get_amount_sat() msg = _("Pay lightning invoice?") + '\n\n' + _("This will send {}?").format(self.app.format_amount_and_units_with_fiat(amount_sat)) +'\n' self.app.protected(msg, self._do_pay_lightning, (invoice,)) else: self.app.show_error(_("Lightning payments are not available for this wallet")) else: self._do_pay_onchain(invoice)
def get_card(self, addr, balance, is_used, label): ci = {} ci['screen'] = self ci['address'] = addr ci['memo'] = label ci['amount'] = self.app.format_amount_and_units(balance) ci['status'] = _('Used') if is_used else _( 'Funded') if balance > 0 else _('Unused') ci['is_frozen'] = self.app.wallet.is_frozen_address(addr) return ci
def close(self): dialog = ChoiceDialog(title=_('Close channel'), choices={ 0: _('Cooperative close'), 1: _('Request force-close') }, key=0, callback=self._close, description=_(messages.MSG_REQUEST_FORCE_CLOSE), keep_choice_order=True) dialog.open()
def routing_dialog(self, item, dt): description = _(messages.MSG_HELP_TRAMPOLINE) def cb(text): self.app.use_gossip = (text == 'Gossip') dialog = ChoiceDialog( _('Lightning Routing'), ['Trampoline', 'Gossip'], 'Gossip' if self.app.use_gossip else 'Trampoline', cb, description=description, keep_choice_order=True) dialog.open()
def update_status(self): invoice = self.app.wallet.get_invoice(self.key) self.status = self.app.wallet.get_invoice_status(invoice) self.status_str = invoice.get_status_str(self.status) self.status_color = pr_color[self.status] self.can_pay = self.status in [PR_UNPAID, PR_FAILED] if self.can_pay and self.is_lightning and self.app.wallet.lnworker: if self.amount_sat and self.amount_sat > self.app.wallet.lnworker.num_sats_can_send( ): self.warning = _('Warning') + ': ' + _( 'This amount exceeds the maximum you can currently send with your channels' )
def __init__(self, msg, callback, *, yes_str: str = None, no_str: str = None, title: str = None): Factory.Popup.__init__(self) self.yes_str = yes_str or _('Yes') self.no_str = no_str or _('No') self.title = title or _('Question') self.message = msg self.callback = callback
def maybe_show_funding_tx(self, chan, funding_tx): n = chan.constraints.funding_txn_minimum_depth message = '\n'.join([ _('Channel established.'), _('Remote peer ID') + ':' + chan.node_id.hex(), _('This channel will be usable after {} confirmations').format(n) ]) if not funding_tx.is_complete(): message += '\n\n' + _('Please sign and broadcast the funding transaction') self.app.show_info(message) if not funding_tx.is_complete(): self.app.tx_dialog(funding_tx)
def _request_force_close(self, b): if not b: return loop = self.app.wallet.network.asyncio_loop coro = asyncio.run_coroutine_threadsafe( self.app.wallet.lnworker.request_force_close(self.chan.channel_id), loop) try: coro.result(5) self.app.show_info(_('Request sent')) except Exception as e: self.logger.exception("Could not close channel") self.app.show_info(_('Could not close channel: ') + repr(e)) # repr because str(Exception()) == ''
def export_backup(self): text = self.app.wallet.lnworker.export_channel_backup( self.chan.channel_id) # TODO: some messages are duplicated between Kivy and Qt. help_text = ' '.join([ _("Channel backups can be imported in another instance of the same wallet, by scanning this QR code." ), _("Please note that channel backups cannot be used to restore your channels." ), _("If you lose your wallet file, the only thing you can do with a backup is to request your channel to be closed, so that your funds will be sent on-chain." ), ]) self.app.qr_dialog(_("Channel Backup " + self.chan.short_id_for_GUI()), text, help_text=help_text)
def on_password(self, pw: str): # if not self.require_password: self.success = True self.message = _('Please wait...') self.dismiss() return # if setting new generic password, enforce min length if self.level > 0: if len(pw) < 6: self.app.show_error( _('Password is too short (min {} characters)').format(6)) return # don't enforce minimum length on existing self.do_check(pw)
def remove_local_tx(self): txid = self.tx.txid() num_child_txs = len(self.wallet.get_depending_transactions(txid)) question = _("Are you sure you want to remove this transaction?") if num_child_txs > 0: question = (_("Are you sure you want to remove this transaction and {} child transactions?") .format(num_child_txs)) def on_prompt(b): if b: self.wallet.remove_transaction(txid) self.wallet.save_db() self.app._trigger_update_wallet() # FIXME private... self.dismiss() d = Question(question, on_prompt) d.open()
def update_status(self): req = self.app.wallet.get_request(self.key) self.status = self.app.wallet.get_request_status(self.key) self.status_str = req.get_status_str(self.status) self.status_color = pr_color[self.status] if self.status == PR_UNPAID and self.is_lightning and self.app.wallet.lnworker: if self.amount_sat and self.amount_sat > self.app.wallet.lnworker.num_sats_can_receive( ): self.warning = _('Warning') + ': ' + _( 'This amount exceeds the maximum you can currently receive with your channels' ) if self.status == PR_UNPAID and not self.is_lightning: address = req.get_address() if self.app.wallet.is_used(address): self.warning = _('Warning') + ': ' + _( 'This address is being reused')
def _do_force_close(self, b): if not b: return loop = self.app.wallet.network.asyncio_loop coro = asyncio.run_coroutine_threadsafe( self.app.wallet.lnworker.force_close_channel(self.chan.channel_id), loop) try: coro.result(1) self.app.show_info( _('Channel closed, you may need to wait at least {} blocks, because of CSV delays' .format(self.chan.config[REMOTE].to_self_delay))) except Exception as e: self.logger.exception("Could not force close channel") self.app.show_info(_('Could not force close channel: ') + repr(e)) # repr because str(Exception()) == ''
def get_card(self, item: Invoice) -> Dict[str, Any]: status = self.app.wallet.get_invoice_status(item) status_str = item.get_status_str(status) is_lightning = item.type == PR_TYPE_LN key = self.app.wallet.get_key_for_outgoing_invoice(item) if is_lightning: assert isinstance(item, LNInvoice) address = item.rhash if self.app.wallet.lnworker: log = self.app.wallet.lnworker.logs.get(key) if status == PR_INFLIGHT and log: status_str += '... (%d)'%len(log) is_bip70 = False else: assert isinstance(item, OnchainInvoice) address = item.get_address() is_bip70 = bool(item.bip70) return { 'is_lightning': is_lightning, 'is_bip70': is_bip70, 'screen': self, 'status': status, 'status_str': status_str, 'key': key, 'memo': item.message or _('No Description'), 'address': address, 'amount': self.app.format_amount_and_units(item.get_amount_sat() or 0), }
def _do_pay_onchain(self, invoice: OnchainInvoice) -> None: outputs = invoice.outputs amount = sum(map(lambda x: x.value, outputs)) if not any(parse_max_spend(x.value) for x in outputs) else '!' coins = self.app.wallet.get_spendable_coins(None) make_tx = lambda rbf: self.app.wallet.make_unsigned_transaction(coins=coins, outputs=outputs, rbf=rbf) on_pay = lambda tx: self.app.protected(_('Send payment?'), self.send_tx, (tx, invoice)) d = ConfirmTxDialog(self.app, amount=amount, make_tx=make_tx, on_pay=on_pay) d.open()
def show_log(self): if self.log: log_str = _('Payment log:') + '\n\n' for payment_attempt_log in self.log: route_str, chan_str, message = payment_attempt_log.formatted_tuple( ) log_str += chan_str + ' --- ' + message + '\n' self.app.show_info(log_str)
def send_report(self): try: loop = self.main_window.network.asyncio_loop proxy = self.main_window.network.proxy # FIXME network request in GUI thread... response = json.loads(BaseCrashReporter.send_report(self, loop, proxy, "/crash.json", timeout=10)) except (ValueError, ClientError) as e: self.logger.warning(f"Error sending crash report. exc={e!r}") self.show_popup(_('Unable to send report'), _("Please check your network connection.")) else: self.show_popup(_('Report sent'), response["text"]) location = response["location"] if location: self.logger.info(f"Crash report sent. location={location!r}") self.open_url(location) self.dismiss()
class PincodeDialog(AbstractPasswordDialog): enter_pw_message = _('Enter your PIN') enter_new_pw_message = _('Enter new PIN') confirm_new_pw_message = _('Confirm new PIN') wrong_password_message = _('Wrong PIN') allow_disable = True def __init__(self, app, **kwargs): AbstractPasswordDialog.__init__(self, app, **kwargs) def clear_password(self): self.ids.kb.password = '' def on_password(self, pw: str): # PIN codes are exactly 6 chars if len(pw) >= 6: self.do_check(pw)
def unit_dialog(self, item, dt): if self._unit_dialog is None: def cb(text): self.app._set_bu(text) item.bu = self.app.base_unit self._unit_dialog = ChoiceDialog(_('Denomination'), base_units_list, self.app.base_unit, cb, keep_choice_order=True) self._unit_dialog.open()
def fx_status(self): fx = self.app.fx if fx.is_enabled(): source = fx.exchange.name() ccy = fx.get_currency() return '%s [%s]' %(ccy, source) else: return _('None')
def _close(self, choice): loop = self.app.wallet.network.asyncio_loop if choice == 1: coro = self.app.wallet.lnworker.request_force_close( self.chan.channel_id) msg = _('Request sent') else: coro = self.app.wallet.lnworker.close_channel(self.chan.channel_id) msg = _('Channel closed') f = asyncio.run_coroutine_threadsafe(coro, loop) try: f.result(5) self.app.show_info(msg) except Exception as e: self.logger.exception("Could not close channel") self.app.show_info(_('Could not close channel: ') + repr(e)) # repr because str(Exception()) == ''
def coinselect_dialog(self, item, dt): if self._coinselect_dialog is None: choosers = sorted(coinchooser.COIN_CHOOSERS.keys()) chooser_name = coinchooser.get_name(self.config) def cb(text): self.config.set_key('coin_chooser', text) item.status = text self._coinselect_dialog = ChoiceDialog(_('Coin selection'), choosers, chooser_name, cb) self._coinselect_dialog.open()
def language_dialog(self, item, dt): if self._language_dialog is None: l = self.config.get('language') or '' def cb(key): self.config.set_key("language", key, True) item.lang = self.get_language_name() self.app.language = key self._language_dialog = ChoiceDialog(_('Language'), languages, l, cb) self._language_dialog.open()
def on_address(self, addr): req = self.app.wallet.get_request(addr) self.status = '' if req: self.message = req.get('memo', '') amount = req.get('amount') self.amount = self.app.format_amount_and_units(amount) if amount else '' status = req.get('status', PR_UNKNOWN) self.status = _('Payment received') if status == PR_PAID else ''
def on_currency(self, ccy): b = (ccy != _('None')) self.fx.set_enabled(b) if b: if ccy != self.fx.get_currency(): self.fx.set_currency(ccy) self.app.fiat_unit = ccy else: self.app.is_fiat = False Clock.schedule_once(lambda dt: self.add_exchanges())
def label_dialog(self): from .label_dialog import LabelDialog key = self.tx.txid() text = self.app.wallet.get_label_for_txid(key) def callback(text): self.app.wallet.set_label(key, text) self.update() self.app.history_screen.update() d = LabelDialog(_('Enter Transaction Label'), text, callback) d.open()