Browse Source

On Android, if channels are not recoverable, display channel backup when a new channel is created.

Remove the 'android_backups' setting, which was unpractical.
patch-4
ThomasV 4 years ago
parent
commit
2fee920f43
  1. 21
      electrum/gui/kivy/main_window.py
  2. 23
      electrum/gui/kivy/uix/dialogs/lightning_open_channel.py
  3. 7
      electrum/gui/kivy/uix/dialogs/settings.py
  4. 1
      electrum/gui/kivy/uix/ui_screens/status.kv
  5. 16
      electrum/gui/qt/main_window.py
  6. 6
      electrum/lnworker.py
  7. 4
      electrum/util.py
  8. 6
      electrum/wallet.py

21
electrum/gui/kivy/main_window.py

@ -193,10 +193,6 @@ class ElectrumWindow(App, Logger):
self.network.run_from_another_thread( self.network.run_from_another_thread(
self.network.stop_gossip()) self.network.stop_gossip())
android_backups = BooleanProperty(False)
def on_android_backups(self, instance, x):
self.electrum_config.set_key('android_backups', self.android_backups, True)
use_change = BooleanProperty(False) use_change = BooleanProperty(False)
def on_use_change(self, instance, x): def on_use_change(self, instance, x):
if self.wallet: if self.wallet:
@ -407,7 +403,6 @@ class ElectrumWindow(App, Logger):
self.daemon = self.gui_object.daemon self.daemon = self.gui_object.daemon
self.fx = self.daemon.fx self.fx = self.daemon.fx
self.use_rbf = config.get('use_rbf', True) self.use_rbf = config.get('use_rbf', True)
self.android_backups = config.get('android_backups', False)
self.use_gossip = config.get('use_gossip', False) self.use_gossip = config.get('use_gossip', False)
self.use_unconfirmed = not config.get('confirmed_only', False) self.use_unconfirmed = not config.get('confirmed_only', False)
@ -1300,9 +1295,14 @@ class ElectrumWindow(App, Logger):
def save_backup(self): def save_backup(self):
if platform != 'android': if platform != 'android':
self._save_backup() backup_dir = util.get_backup_dir(self.electrum_config)
if backup_dir:
self._save_backup(backup_dir)
else:
self.show_error(_("Backup NOT saved. Backup directory not configured."))
return return
backup_dir = util.android_backup_dir()
from android.permissions import request_permissions, Permission from android.permissions import request_permissions, Permission
def cb(permissions, grant_results: Sequence[bool]): def cb(permissions, grant_results: Sequence[bool]):
if not grant_results or not grant_results[0]: if not grant_results or not grant_results[0]:
@ -1313,17 +1313,14 @@ class ElectrumWindow(App, Logger):
Clock.schedule_once(lambda dt: self._save_backup()) Clock.schedule_once(lambda dt: self._save_backup())
request_permissions([Permission.WRITE_EXTERNAL_STORAGE], cb) request_permissions([Permission.WRITE_EXTERNAL_STORAGE], cb)
def _save_backup(self): def _save_backup(self, backup_dir):
try: try:
new_path = self.wallet.save_backup() new_path = self.wallet.save_backup(backup_dir)
except Exception as e: except Exception as e:
self.logger.exception("Failed to save wallet backup") self.logger.exception("Failed to save wallet backup")
self.show_error("Failed to save wallet backup" + '\n' + str(e)) self.show_error("Failed to save wallet backup" + '\n' + str(e))
return return
if new_path: self.show_info(_("Backup saved:") + f"\n{new_path}")
self.show_info(_("Backup saved:") + f"\n{new_path}")
else:
self.show_error(_("Backup NOT saved. Backup directory not configured."))
def export_private_keys(self, pk_label, addr): def export_private_keys(self, pk_label, addr):
if self.wallet.is_watching_only(): if self.wallet.is_watching_only():

23
electrum/gui/kivy/uix/dialogs/lightning_open_channel.py

@ -13,6 +13,7 @@ from electrum.lnutil import ln_dummy_address, extract_nodeid
from .label_dialog import LabelDialog from .label_dialog import LabelDialog
from .confirm_tx_dialog import ConfirmTxDialog from .confirm_tx_dialog import ConfirmTxDialog
from .qr_dialog import QRDialog
if TYPE_CHECKING: if TYPE_CHECKING:
from ...main_window import ElectrumWindow from ...main_window import ElectrumWindow
@ -208,6 +209,27 @@ class LightningOpenChannelDialog(Factory.Popup, Logger):
self.app.logger.exception("Problem opening channel") self.app.logger.exception("Problem opening channel")
self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e))
return return
# TODO: it would be nice to show this before broadcasting
if lnworker.has_recoverable_channels():
self.maybe_show_funding_tx(chan, funding_tx)
else:
title = _('Save backup')
help_text = ' '.join([
_('Your wallet does not have recoverable channels.'),
_('Please save this channel backup on another device.'),
_('It may be imported in another Electrum wallet with the same seed.')
])
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 maybe_show_funding_tx(self, chan, funding_tx):
n = chan.constraints.funding_txn_minimum_depth n = chan.constraints.funding_txn_minimum_depth
message = '\n'.join([ message = '\n'.join([
_('Channel established.'), _('Channel established.'),
@ -217,5 +239,6 @@ class LightningOpenChannelDialog(Factory.Popup, Logger):
if not funding_tx.is_complete(): if not funding_tx.is_complete():
message += '\n\n' + _('Please sign and broadcast the funding transaction') message += '\n\n' + _('Please sign and broadcast the funding transaction')
self.app.show_info(message) self.app.show_info(message)
if not funding_tx.is_complete(): if not funding_tx.is_complete():
self.app.tx_dialog(funding_tx) self.app.tx_dialog(funding_tx)

7
electrum/gui/kivy/uix/dialogs/settings.py

@ -93,13 +93,6 @@ Builder.load_string('''
title: _('Lightning Routing') + ': ' + self.status title: _('Lightning Routing') + ': ' + self.status
description: _("Use trampoline routing or gossip.") description: _("Use trampoline routing or gossip.")
action: partial(root.routing_dialog, self) action: partial(root.routing_dialog, self)
CardSeparator
SettingsItem:
status: _('Yes') if app.android_backups else _('No')
title: _('Backups') + ': ' + self.status
description: _("Backup wallet to external storage.")
message: _("If this option is checked, a backup of your wallet will be written to external storage everytime you create a new channel. Make sure your wallet is protected with a strong password before you enable this option.")
action: partial(root.boolean_dialog, 'android_backups', _('Backups'), self.message)
# disabled: there is currently only one coin selection policy # disabled: there is currently only one coin selection policy
#CardSeparator #CardSeparator

1
electrum/gui/kivy/uix/ui_screens/status.kv

@ -81,7 +81,6 @@ Popup:
size_hint: 0.5, None size_hint: 0.5, None
height: '48dp' height: '48dp'
text: _('Export Backup') text: _('Export Backup')
disabled: not app.android_backups
on_release: on_release:
root.dismiss() root.dismiss()
app.save_backup() app.save_backup()

16
electrum/gui/qt/main_window.py

@ -627,18 +627,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
if not d.exec_(): if not d.exec_():
return False return False
backup_dir = get_backup_dir(self.config)
if backup_dir is not None:
self.show_message(_("You need to configure a backup directory in your preferences"), title=_("Backup not configured"))
return
try: try:
new_path = self.wallet.save_backup() new_path = self.wallet.save_backup(backup_dir)
except BaseException as reason: except BaseException as reason:
self.show_critical(_("Electrum was unable to copy your wallet file to the specified location.") + "\n" + str(reason), title=_("Unable to create backup")) self.show_critical(_("Electrum was unable to copy your wallet file to the specified location.") + "\n" + str(reason), title=_("Unable to create backup"))
return return
if new_path: msg = _("A copy of your wallet file was created in")+" '%s'" % str(new_path)
msg = _("A copy of your wallet file was created in")+" '%s'" % str(new_path) self.show_message(msg, title=_("Wallet backup created"))
self.show_message(msg, title=_("Wallet backup created")) return True
return True
else:
self.show_message(_("You need to configure a backup directory in your preferences"), title=_("Backup not created"))
return False
def update_recently_visited(self, filename): def update_recently_visited(self, filename):
recent = self.config.get('recently_open', []) recent = self.config.get('recently_open', [])

6
electrum/lnworker.py

@ -26,7 +26,7 @@ from aiorpcx import run_in_thread, TaskGroup, NetAddress, ignore_after
from . import constants, util from . import constants, util
from . import keystore from . import keystore
from .util import profiler, chunks from .util import profiler, chunks, get_backup_dir
from .invoices import PR_TYPE_LN, PR_UNPAID, PR_EXPIRED, PR_PAID, PR_INFLIGHT, PR_FAILED, PR_ROUTING, LNInvoice, LN_EXPIRY_NEVER from .invoices import PR_TYPE_LN, PR_UNPAID, PR_EXPIRED, PR_PAID, PR_INFLIGHT, PR_FAILED, PR_ROUTING, LNInvoice, LN_EXPIRY_NEVER
from .util import NetworkRetryManager, JsonRPCClient from .util import NetworkRetryManager, JsonRPCClient
from .lnutil import LN_MAX_FUNDING_SAT from .lnutil import LN_MAX_FUNDING_SAT
@ -1003,7 +1003,9 @@ class LNWallet(LNWorker):
self.wallet.set_reserved_state_of_address(addr, reserved=True) self.wallet.set_reserved_state_of_address(addr, reserved=True)
try: try:
self.save_channel(chan) self.save_channel(chan)
self.wallet.save_backup() backup_dir = get_backup_dir(self.config)
if backup_dir is not None:
self.wallet.save_backup(backup_dir)
except: except:
chan.set_state(ChannelState.REDEEMED) chan.set_state(ChannelState.REDEEMED)
self.remove_channel(chan.channel_id) self.remove_channel(chan.channel_id)

4
electrum/util.py

@ -428,8 +428,10 @@ def android_data_dir():
return PythonActivity.mActivity.getFilesDir().getPath() + '/data' return PythonActivity.mActivity.getFilesDir().getPath() + '/data'
def get_backup_dir(config): def get_backup_dir(config):
# this is used to save a backup everytime a channel is created
# on Android, the export backup button uses android_backup_dir()
if 'ANDROID_DATA' in os.environ: if 'ANDROID_DATA' in os.environ:
return android_backup_dir() if config.get('android_backups') else None return None
else: else:
return config.get('backup_dir') return config.get('backup_dir')

6
electrum/wallet.py

@ -57,7 +57,6 @@ from .util import (NotEnoughFunds, UserCancelled, profiler,
WalletFileException, BitcoinException, MultipleSpendMaxTxOutputs, WalletFileException, BitcoinException, MultipleSpendMaxTxOutputs,
InvalidPassword, format_time, timestamp_to_datetime, Satoshis, InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex) Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex)
from .util import get_backup_dir
from .simple_config import SimpleConfig, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE from .simple_config import SimpleConfig, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
from .bitcoin import COIN, TYPE_ADDRESS from .bitcoin import COIN, TYPE_ADDRESS
from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold
@ -315,10 +314,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
if self.storage: if self.storage:
self.db.write(self.storage) self.db.write(self.storage)
def save_backup(self): def save_backup(self, backup_dir):
backup_dir = get_backup_dir(self.config)
if backup_dir is None:
return
new_db = WalletDB(self.db.dump(), manual_upgrades=False) new_db = WalletDB(self.db.dump(), manual_upgrades=False)
if self.lnworker: if self.lnworker:

Loading…
Cancel
Save