diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index a07469a92..43bc5585e 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -233,6 +233,7 @@ class ElectrumGui(Logger): run_hook('on_new_window', w) w.warn_if_testnet() w.warn_if_watching_only() + w.warn_if_lightning_backup() return w def count_wizards_in_progress(func): diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py index be34aa9d7..255ff251d 100644 --- a/electrum/gui/qt/channels_list.py +++ b/electrum/gui/qt/channels_list.py @@ -8,7 +8,7 @@ from PyQt5.QtWidgets import QMenu, QHBoxLayout, QLabel, QVBoxLayout, QGridLayout from electrum.util import bh2u, NotEnoughFunds, NoDynamicFeeEstimates from electrum.i18n import _ -from electrum.lnchannel import Channel +from electrum.lnchannel import Channel, peer_states from electrum.wallet import Abstract_Wallet from electrum.lnutil import LOCAL, REMOTE, format_short_channel_id, LN_MAX_FUNDING_SAT @@ -84,10 +84,14 @@ class ChannelsList(MyTreeView): WaitingDialog(self, 'please wait..', task, self.on_success, self.on_failure) def force_close(self, channel_id): - def task(): - coro = self.lnworker.force_close_channel(channel_id) - return self.network.run_from_another_thread(coro) - if self.parent.question('Force-close channel?\nReclaimed funds will not be immediately available.'): + if self.lnworker.wallet.is_lightning_backup(): + msg = _('WARNING: force-closing from an old state might result in fund loss.\nAre you sure?') + else: + msg = _('Force-close channel?\nReclaimed funds will not be immediately available.') + if self.parent.question(msg): + def task(): + coro = self.lnworker.force_close_channel(channel_id) + return self.network.run_from_another_thread(coro) WaitingDialog(self, 'please wait..', task, self.on_success, self.on_failure) def remove_channel(self, channel_id): @@ -105,7 +109,8 @@ class ChannelsList(MyTreeView): menu.addAction(_("Details..."), lambda: self.details(channel_id)) self.add_copy_menu(menu, idx) if not chan.is_closed(): - menu.addAction(_("Close channel"), lambda: self.close_channel(channel_id)) + if chan.peer_state == peer_states.GOOD: + menu.addAction(_("Close channel"), lambda: self.close_channel(channel_id)) menu.addAction(_("Force-close channel"), lambda: self.force_close(channel_id)) else: menu.addAction(_("Remove"), lambda: self.remove_channel(channel_id)) diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 65d8644f9..bbfacbee1 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -513,6 +513,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): ]) self.show_warning(msg, title=_('Watch-only wallet')) + def warn_if_lightning_backup(self): + if self.wallet.is_lightning_backup(): + msg = '\n\n'.join([ + _("This file is a backup of a lightning wallet."), + _("You will not be able to perform lightning payments using this file, and the lightning balance displayed in this wallet might be outdated.") + ' ' + \ + _("If you have lost the original wallet file, you can use this file to trigger a forced closure of your channels."), + _("Do you want to have your channels force-closed?") + ]) + if self.question(msg, title=_('Lightning Backup')): + self.network.maybe_init_lightning() + self.wallet.lnworker.start_network(self.network) + def warn_if_testnet(self): if not constants.net.TESTNET: return @@ -549,7 +561,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): return self.gui_object.new_window(filename) - def backup_wallet(self): path = self.wallet.storage.path wallet_folder = os.path.dirname(path) @@ -557,12 +568,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): if not filename: return new_path = os.path.join(wallet_folder, filename) - if new_path != path: - try: - shutil.copy2(path, new_path) - self.show_message(_("A copy of your wallet file was created in")+" '%s'" % str(new_path), title=_("Wallet backup created")) - 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")) + if new_path == path: + return + try: + self.wallet.save_backup(new_path) + 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")) + return + self.show_message(_("A copy of your wallet file was created in")+" '%s'" % str(new_path), title=_("Wallet backup created")) def update_recently_visited(self, filename): recent = self.config.get('recently_open', []) diff --git a/electrum/wallet.py b/electrum/wallet.py index 6543e5498..542cd1178 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -263,6 +263,13 @@ class Abstract_Wallet(AddressSynchronizer, ABC): if self.storage: self.db.write(self.storage) + def save_backup(self, path): + # fixme: we need to change password... + new_storage = WalletStorage(path) + self.db.put('is_backup', True) + self.db.write(new_storage) + self.db.put('is_backup', None) + def has_lightning(self): return bool(self.lnworker) @@ -285,6 +292,9 @@ class Abstract_Wallet(AddressSynchronizer, ABC): self.db.put('lightning_privkey2', None) self.save_db() + def is_lightning_backup(self): + return self.has_lightning() and self.db.get('is_backup') + def stop_threads(self): super().stop_threads() if any([ks.is_requesting_to_be_rewritten_to_wallet_file for ks in self.get_keystores()]): @@ -301,7 +311,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC): def start_network(self, network): AddressSynchronizer.start_network(self, network) - if self.lnworker and network: + if self.lnworker and network and not self.is_lightning_backup(): network.maybe_init_lightning() self.lnworker.start_network(network)