qml: add PIN auth to close channel operation.
This commit is contained in:
@@ -18,7 +18,6 @@ ElDialog {
|
|||||||
title: qsTr('Close Channel')
|
title: qsTr('Close Channel')
|
||||||
iconSource: Qt.resolvedUrl('../../icons/lightning_disconnected.png')
|
iconSource: Qt.resolvedUrl('../../icons/lightning_disconnected.png')
|
||||||
|
|
||||||
property bool _closing: false
|
|
||||||
property string _closing_method
|
property string _closing_method
|
||||||
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
@@ -114,21 +113,21 @@ ElDialog {
|
|||||||
id: closetypeCoop
|
id: closetypeCoop
|
||||||
ButtonGroup.group: closetypegroup
|
ButtonGroup.group: closetypegroup
|
||||||
property string closetype: 'cooperative'
|
property string closetype: 'cooperative'
|
||||||
enabled: !_closing && channeldetails.canCoopClose
|
enabled: !channeldetails.isClosing && channeldetails.canCoopClose
|
||||||
text: qsTr('Cooperative close')
|
text: qsTr('Cooperative close')
|
||||||
}
|
}
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: closetypeRemoteForce
|
id: closetypeRemoteForce
|
||||||
ButtonGroup.group: closetypegroup
|
ButtonGroup.group: closetypegroup
|
||||||
property string closetype: 'remote_force'
|
property string closetype: 'remote_force'
|
||||||
enabled: !_closing && channeldetails.canForceClose
|
enabled: !channeldetails.isClosing && channeldetails.canForceClose
|
||||||
text: qsTr('Request Force-close')
|
text: qsTr('Request Force-close')
|
||||||
}
|
}
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: closetypeLocalForce
|
id: closetypeLocalForce
|
||||||
ButtonGroup.group: closetypegroup
|
ButtonGroup.group: closetypegroup
|
||||||
property string closetype: 'local_force'
|
property string closetype: 'local_force'
|
||||||
enabled: !_closing && channeldetails.canForceClose && !channeldetails.isBackup
|
enabled: !channeldetails.isClosing && channeldetails.canForceClose && !channeldetails.isBackup
|
||||||
text: qsTr('Local Force-close')
|
text: qsTr('Local Force-close')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,17 +140,17 @@ ElDialog {
|
|||||||
id: errorText
|
id: errorText
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.maximumWidth: parent.width
|
Layout.maximumWidth: parent.width
|
||||||
visible: !_closing && errorText.text
|
visible: !channeldetails.isClosing && errorText.text
|
||||||
iconStyle: InfoTextArea.IconStyle.Error
|
iconStyle: InfoTextArea.IconStyle.Error
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr('Closing...')
|
text: qsTr('Closing...')
|
||||||
visible: _closing
|
visible: channeldetails.isClosing
|
||||||
}
|
}
|
||||||
BusyIndicator {
|
BusyIndicator {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: _closing
|
visible: channeldetails.isClosing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,10 +161,10 @@ ElDialog {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr('Close channel')
|
text: qsTr('Close channel')
|
||||||
icon.source: '../../icons/closebutton.png'
|
icon.source: '../../icons/closebutton.png'
|
||||||
enabled: !_closing
|
enabled: !channeldetails.isClosing
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (closetypegroup.checkedButton.closetype == 'local_force') {
|
if (closetypegroup.checkedButton.closetype == 'local_force') {
|
||||||
showBackupThenConfirmClose()
|
showBackupThenClose()
|
||||||
} else {
|
} else {
|
||||||
doCloseChannel()
|
doCloseChannel()
|
||||||
}
|
}
|
||||||
@@ -173,7 +172,7 @@ ElDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showBackupThenConfirmClose() {
|
function showBackupThenClose() {
|
||||||
var sharedialog = app.genericShareDialog.createObject(app, {
|
var sharedialog = app.genericShareDialog.createObject(app, {
|
||||||
title: qsTr('Save channel backup and force close'),
|
title: qsTr('Save channel backup and force close'),
|
||||||
text_qr: channeldetails.channelBackup(),
|
text_qr: channeldetails.channelBackup(),
|
||||||
@@ -181,24 +180,12 @@ ElDialog {
|
|||||||
helpTextIconStyle: InfoTextArea.IconStyle.Warn
|
helpTextIconStyle: InfoTextArea.IconStyle.Warn
|
||||||
})
|
})
|
||||||
sharedialog.closed.connect(function() {
|
sharedialog.closed.connect(function() {
|
||||||
confirmCloseChannel()
|
doCloseChannel()
|
||||||
})
|
})
|
||||||
sharedialog.open()
|
sharedialog.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmCloseChannel() {
|
|
||||||
var confirmdialog = app.messageDialog.createObject(app, {
|
|
||||||
title: qsTr('Confirm force close?'),
|
|
||||||
yesno: true
|
|
||||||
})
|
|
||||||
confirmdialog.accepted.connect(function() {
|
|
||||||
doCloseChannel()
|
|
||||||
})
|
|
||||||
confirmdialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
function doCloseChannel() {
|
function doCloseChannel() {
|
||||||
_closing = true
|
|
||||||
_closing_method = closetypegroup.checkedButton.closetype
|
_closing_method = closetypegroup.checkedButton.closetype
|
||||||
channeldetails.closeChannel(_closing_method)
|
channeldetails.closeChannel(_closing_method)
|
||||||
}
|
}
|
||||||
@@ -215,8 +202,12 @@ ElDialog {
|
|||||||
wallet: Daemon.currentWallet
|
wallet: Daemon.currentWallet
|
||||||
channelid: dialog.channelid
|
channelid: dialog.channelid
|
||||||
|
|
||||||
|
onAuthRequired: {
|
||||||
|
app.handleAuthRequired(channeldetails, method, authMessage)
|
||||||
|
}
|
||||||
|
|
||||||
onChannelChanged: {
|
onChannelChanged: {
|
||||||
if (!channeldetails.canClose)
|
if (!channeldetails.canClose || channeldetails.isClosing)
|
||||||
return
|
return
|
||||||
|
|
||||||
// init default choice
|
// init default choice
|
||||||
@@ -227,7 +218,6 @@ ElDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onChannelCloseSuccess: {
|
onChannelCloseSuccess: {
|
||||||
_closing = false
|
|
||||||
if (_closing_method == 'local_force') {
|
if (_closing_method == 'local_force') {
|
||||||
showCloseMessage(qsTr('Channel closed. You may need to wait at least %1 blocks, because of CSV delays').arg(channeldetails.toSelfDelay))
|
showCloseMessage(qsTr('Channel closed. You may need to wait at least %1 blocks, because of CSV delays').arg(channeldetails.toSelfDelay))
|
||||||
} else if (_closing_method == 'remote_force') {
|
} else if (_closing_method == 'remote_force') {
|
||||||
@@ -239,7 +229,6 @@ ElDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onChannelCloseFailed: {
|
onChannelCloseFailed: {
|
||||||
_closing = false
|
|
||||||
errorText.text = message
|
errorText.text = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import asyncio
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS
|
||||||
@@ -9,12 +8,13 @@ from electrum.logging import get_logger
|
|||||||
from electrum.lnutil import LOCAL, REMOTE
|
from electrum.lnutil import LOCAL, REMOTE
|
||||||
from electrum.lnchannel import ChanCloseOption, ChannelState
|
from electrum.lnchannel import ChanCloseOption, ChannelState
|
||||||
|
|
||||||
|
from .auth import AuthMixin, auth_protect
|
||||||
from .qewallet import QEWallet
|
from .qewallet import QEWallet
|
||||||
from .qetypes import QEAmount
|
from .qetypes import QEAmount
|
||||||
from .util import QtEventListener, qt_event_listener, event_listener
|
from .util import QtEventListener, event_listener
|
||||||
|
|
||||||
|
|
||||||
class QEChannelDetails(QObject, QtEventListener):
|
class QEChannelDetails(AuthMixin, QObject, QtEventListener):
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
class State: # subset, only ones we currently need in UI
|
class State: # subset, only ones we currently need in UI
|
||||||
@@ -26,6 +26,7 @@ class QEChannelDetails(QObject, QtEventListener):
|
|||||||
channelChanged = pyqtSignal()
|
channelChanged = pyqtSignal()
|
||||||
channelCloseSuccess = pyqtSignal()
|
channelCloseSuccess = pyqtSignal()
|
||||||
channelCloseFailed = pyqtSignal([str], arguments=['message'])
|
channelCloseFailed = pyqtSignal([str], arguments=['message'])
|
||||||
|
isClosingChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -39,6 +40,7 @@ class QEChannelDetails(QObject, QtEventListener):
|
|||||||
self._remote_capacity = QEAmount()
|
self._remote_capacity = QEAmount()
|
||||||
self._can_receive = QEAmount()
|
self._can_receive = QEAmount()
|
||||||
self._can_send = QEAmount()
|
self._can_send = QEAmount()
|
||||||
|
self._is_closing = False
|
||||||
|
|
||||||
self.register_callbacks()
|
self.register_callbacks()
|
||||||
self.destroyed.connect(lambda: self.on_destroy())
|
self.destroyed.connect(lambda: self.on_destroy())
|
||||||
@@ -192,6 +194,12 @@ class QEChannelDetails(QObject, QtEventListener):
|
|||||||
def toSelfDelay(self):
|
def toSelfDelay(self):
|
||||||
return self._channel.config[REMOTE].to_self_delay
|
return self._channel.config[REMOTE].to_self_delay
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=isClosingChanged)
|
||||||
|
def isClosing(self):
|
||||||
|
# Note: isClosing only applies to a closing action started by this instance, not
|
||||||
|
# whether the channel is closing
|
||||||
|
return self._is_closing
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def freezeForSending(self):
|
def freezeForSending(self):
|
||||||
lnworker = self._channel.lnworker
|
lnworker = self._channel.lnworker
|
||||||
@@ -212,19 +220,30 @@ class QEChannelDetails(QObject, QtEventListener):
|
|||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def closeChannel(self, closetype):
|
def closeChannel(self, closetype):
|
||||||
|
self.do_close_channel(closetype)
|
||||||
|
|
||||||
|
@auth_protect(message=_('Close Lightning channel?'))
|
||||||
|
def do_close_channel(self, closetype):
|
||||||
channel_id = self._channel.channel_id
|
channel_id = self._channel.channel_id
|
||||||
|
|
||||||
def do_close():
|
def do_close():
|
||||||
try:
|
try:
|
||||||
|
self._is_closing = True
|
||||||
|
self.isClosingChanged.emit()
|
||||||
if closetype == 'remote_force':
|
if closetype == 'remote_force':
|
||||||
self._wallet.wallet.network.run_from_another_thread(self._wallet.wallet.lnworker.request_force_close(channel_id))
|
self._wallet.wallet.network.run_from_another_thread(self._wallet.wallet.lnworker.request_force_close(channel_id))
|
||||||
elif closetype == 'local_force':
|
elif closetype == 'local_force':
|
||||||
self._wallet.wallet.network.run_from_another_thread(self._wallet.wallet.lnworker.force_close_channel(channel_id))
|
self._wallet.wallet.network.run_from_another_thread(self._wallet.wallet.lnworker.force_close_channel(channel_id))
|
||||||
else:
|
else:
|
||||||
self._wallet.wallet.network.run_from_another_thread(self._wallet.wallet.lnworker.close_channel(channel_id))
|
self._wallet.wallet.network.run_from_another_thread(self._wallet.wallet.lnworker.close_channel(channel_id))
|
||||||
|
self._logger.debug('Channel close successful')
|
||||||
self.channelCloseSuccess.emit()
|
self.channelCloseSuccess.emit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._logger.exception("Could not close channel: " + repr(e))
|
self._logger.exception("Could not close channel: " + repr(e))
|
||||||
self.channelCloseFailed.emit(_('Could not close channel: ') + repr(e))
|
self.channelCloseFailed.emit(_('Could not close channel: ') + repr(e))
|
||||||
|
finally:
|
||||||
|
self._is_closing = False
|
||||||
|
self.isClosingChanged.emit()
|
||||||
|
|
||||||
threading.Thread(target=do_close, daemon=True).start()
|
threading.Thread(target=do_close, daemon=True).start()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user