From 3de498907c7fdf2c7ffbefd3afcb825b4ff5d205 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Thu, 28 Jul 2022 18:43:06 +0200 Subject: [PATCH] qml: delete_wallet and add checks for channels, balance, pending requests --- .../gui/qml/components/NewWalletWizard.qml | 8 +-- electrum/gui/qml/components/Wallets.qml | 20 +++++- electrum/gui/qml/qedaemon.py | 61 ++++++++++++++++--- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/electrum/gui/qml/components/NewWalletWizard.qml b/electrum/gui/qml/components/NewWalletWizard.qml index 61808faf4..95fbe665b 100644 --- a/electrum/gui/qml/components/NewWalletWizard.qml +++ b/electrum/gui/qml/components/NewWalletWizard.qml @@ -44,13 +44,13 @@ Wizard { case 'haveseed': page = _loadNextComponent(components.haveseed, wizard_data) page.next.connect(function() {haveseedDone()}) - if (wizard_data['seed_type'] != 'bip39' && Daemon.singlePassword) + if (wizard_data['seed_type'] != 'bip39' && Daemon.singlePasswordEnabled) page.last = true break case 'masterkey': page = _loadNextComponent(components.havemasterkey, wizard_data) page.next.connect(function() {havemasterkeyDone()}) - if (Daemon.singlePassword) + if (Daemon.singlePasswordEnabled) page.last = true break } @@ -59,7 +59,7 @@ Wizard { function createseedDone(d) { console.log('create seed done') var page = _loadNextComponent(components.confirmseed, wizard_data) - if (Daemon.singlePassword) + if (Daemon.singlePasswordEnabled) page.last = true else page.next.connect(function() {confirmseedDone()}) @@ -75,7 +75,7 @@ Wizard { console.log('have seed done') if (wizard_data['seed_type'] == 'bip39') { var page = _loadNextComponent(components.bip39refine, wizard_data) - if (Daemon.singlePassword) + if (Daemon.singlePasswordEnabled) page.last = true else page.next.connect(function() {bip39refineDone()}) diff --git a/electrum/gui/qml/components/Wallets.qml b/electrum/gui/qml/components/Wallets.qml index 5a3f816d1..578ab3f9b 100644 --- a/electrum/gui/qml/components/Wallets.qml +++ b/electrum/gui/qml/components/Wallets.qml @@ -35,7 +35,7 @@ Pane { var dialog = app.messageDialog.createObject(rootItem, {'text': qsTr('Really delete this wallet?'), 'yesno': true}) dialog.yesClicked.connect(function() { - Daemon.delete_wallet(Daemon.currentWallet) + Daemon.check_then_delete_wallet(Daemon.currentWallet) }) dialog.open() } @@ -320,6 +320,24 @@ Pane { }) dialog.open() } + function onWalletDeleteError(code, message) { + if (code == 'unpaid_requests') { + var dialog = app.messageDialog.createObject(app, {text: message, yesno: true }) + dialog.yesClicked.connect(function() { + Daemon.check_then_delete_wallet(Daemon.currentWallet, true) + }) + dialog.open() + } else if (code == 'balance') { + var dialog = app.messageDialog.createObject(app, {text: message, yesno: true }) + dialog.yesClicked.connect(function() { + Daemon.check_then_delete_wallet(Daemon.currentWallet, true, true) + }) + dialog.open() + } else { + var dialog = app.messageDialog.createObject(app, {text: message }) + dialog.open() + } + } } Connections { diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py index be44a31d9..7a08d23ee 100644 --- a/electrum/gui/qml/qedaemon.py +++ b/electrum/gui/qml/qedaemon.py @@ -3,6 +3,7 @@ import os from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from electrum.i18n import _ from electrum.logging import get_logger from electrum.util import WalletFileException, standardize_path from electrum.wallet import Abstract_Wallet @@ -55,10 +56,28 @@ class QEWalletListModel(QAbstractListModel): wallet_name = os.path.basename(wallet_path) else: wallet_name = wallet.basename() + wallet_path = standardize_path(wallet_path) item = (wallet_name, wallet_path, wallet) self.wallets.append(item); self.endInsertRows(); + def remove_wallet(self, path): + i = 0 + wallets = [] + remove = -1 + for wallet_name, wallet_path, wallet in self.wallets: + if wallet_path == path: + remove = i + else: + self._logger.debug('HM, %s is not %s', wallet_path, path) + wallets.append((wallet_name, wallet_path, wallet)) + i += 1 + + if remove >= 0: + self.beginRemoveRows(QModelIndex(), i, i) + self.wallets = wallets + self.endRemoveRows() + class QEAvailableWalletListModel(QEWalletListModel): def __init__(self, daemon, parent=None): QEWalletListModel.__init__(self, parent) @@ -110,6 +129,7 @@ class QEDaemon(AuthMixin, QObject): availableWalletsChanged = pyqtSignal() walletOpenError = pyqtSignal([str], arguments=["error"]) fxChanged = pyqtSignal() + walletDeleteError = pyqtSignal([str,str], arguments=['code', 'message']) @pyqtSlot() def passwordValidityCheck(self): @@ -144,7 +164,7 @@ class QEDaemon(AuthMixin, QObject): try: wallet = self.daemon.load_wallet(self._path, password) if wallet != None: - self._loaded_wallets.add_wallet(wallet=wallet) + self._loaded_wallets.add_wallet(wallet_path=self._path, wallet=wallet) self._current_wallet = QEWallet.getInstanceFor(wallet) self._current_wallet.password = password self.walletLoaded.emit() @@ -165,19 +185,44 @@ class QEDaemon(AuthMixin, QObject): self._logger.error(str(e)) self.walletOpenError.emit(str(e)) + @pyqtSlot(QEWallet) + @pyqtSlot(QEWallet, bool) + @pyqtSlot(QEWallet, bool, bool) + def check_then_delete_wallet(self, wallet, confirm_requests=False, confirm_balance=False): + if wallet.wallet.lnworker: + lnchannels = wallet.wallet.lnworker.get_channel_objects() + if any([channel.get_state() != ChannelState.REDEEMED for channel in lnchannels.values()]): + self.walletDeleteError.emit('unclosed_channels', _('There are still channels that are not fully closed')) + return + + num_requests = len(wallet.wallet.get_unpaid_requests()) + if num_requests > 0 and not confirm_requests: + self.walletDeleteError.emit('unpaid_requests', _('There are still unpaid requests. Really delete?')) + return + + c, u, x = wallet.wallet.get_balance() + if c+u+x > 0 and not wallet.wallet.is_watching_only() and not confirm_balance: + self.walletDeleteError.emit('balance', _('There are still coins present in this wallet. Really delete?')) + return + + self.delete_wallet(wallet) + @pyqtSlot(QEWallet) @auth_protect def delete_wallet(self, wallet): - path = wallet.wallet.storage.path - self._logger.debug('Ok to delete wallet with path %s' % path) - # TODO checks, e.g. existing LN channels, unpaid requests, etc - self._logger.debug('Not deleting yet, just unloading for now') - # TODO actually delete - # TODO walletLoaded signal is confusing - self.daemon.stop_wallet(path) + path = standardize_path(wallet.wallet.storage.path) + self._logger.debug('deleting wallet with path %s' % path) self._current_wallet = None + # TODO walletLoaded signal is confusing self.walletLoaded.emit() + if not self.daemon.delete_wallet(path): + self.walletDeleteError.emit('error', _('Problem deleting wallet')) + return + + self.activeWallets.remove_wallet(path) + self.availableWallets.remove_wallet(path) + @pyqtProperty('QString') def path(self): return self._path