From f5b1f7d2d9ea2f344593106ba98ff0c72b7dccf8 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 21 Jul 2022 11:41:44 +0200 Subject: [PATCH] Generalize ChannelDetailsDialog to channel backups. Access funding_tx and closing_tx from the dialog, instead of from the channels_list context menu. --- electrum/gui/qt/channel_details.py | 115 +++++++++++++++++++---------- electrum/gui/qt/channels_list.py | 15 +--- electrum/gui/qt/main_window.py | 6 +- 3 files changed, 80 insertions(+), 56 deletions(-) diff --git a/electrum/gui/qt/channel_details.py b/electrum/gui/qt/channel_details.py index 357346b27..d60a211cb 100644 --- a/electrum/gui/qt/channel_details.py +++ b/electrum/gui/qt/channel_details.py @@ -38,6 +38,50 @@ class LinkedLabel(QtWidgets.QLabel): class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener): + def __init__(self, window: 'ElectrumWindow', chan: AbstractChannel): + super().__init__(window) + # initialize instance fields + self.window = window + self.wallet = window.wallet + self.chan = chan + self.format_msat = lambda msat: window.format_amount_and_units(msat / 1000) + self.format_sat = lambda sat: window.format_amount_and_units(sat) + # register callbacks for updating + self.register_callbacks() + title = _('Lightning Channel') if not self.chan.is_backup() else _('Channel Backup') + self.setWindowTitle(title) + self.setMinimumSize(800, 400) + # activity labels. not used for backups. + self.local_balance_label = SelectableLabel() + self.remote_balance_label = SelectableLabel() + self.can_send_label = SelectableLabel() + self.can_receive_label = SelectableLabel() + # add widgets + vbox = QtWidgets.QVBoxLayout(self) + if self.chan.is_backup(): + vbox.addWidget(QLabel('\n'.join([ + _("This is a channel backup."), + _("It shows a channel that was opened with another instance of this wallet"), + _("A backup does not contain information about your local balance in the channel."), + _("You can use it to request a force close.") + ]))) + + form = self.get_common_form(chan) + vbox.addLayout(form) + if not self.chan.is_closed() and not self.chan.is_backup(): + hbox_stats = self.get_hbox_stats(chan) + form.addRow(QLabel(_('Channel stats')+ ':'), hbox_stats) + + if not self.chan.is_backup(): + # add htlc tree view to vbox (wouldn't scale correctly in QFormLayout) + vbox.addWidget(QLabel(_('Payments (HTLCs):'))) + w = self.create_htlc_list(chan) + vbox.addWidget(w) + + vbox.addLayout(Buttons(CloseButton(self))) + # initialize sent/received fields + self.update() + def make_htlc_item(self, i: UpdateAddHtlc, direction: Direction) -> HTLCItem: it = HTLCItem(_('Sent HTLC with ID {}' if Direction.SENT == direction else 'Received HTLC with ID {}').format(i.htlc_id)) it.appendRow([HTLCItem(_('Amount')),HTLCItem(self.format_msat(i.amount_msat))]) @@ -113,6 +157,8 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener): self.update() def update(self): + if self.chan.is_closed() or self.chan.is_backup(): + return self.can_send_label.setText(self.format_msat(self.chan.available_to_spend(LOCAL))) self.can_receive_label.setText(self.format_msat(self.chan.available_to_spend(REMOTE))) self.sent_label.setText(self.format_msat(self.chan.total_msat(Direction.SENT))) @@ -122,55 +168,47 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener): @QtCore.pyqtSlot(str) def show_tx(self, link_text: str): - funding_tx = self.wallet.db.get_transaction(self.chan.funding_outpoint.txid) - if not funding_tx: - self.show_error(_("Funding transaction not found.")) + tx = self.wallet.adb.get_transaction(link_text) + if not tx: + self.show_error(_("Transaction not found.")) return - self.window.show_transaction(funding_tx, tx_desc=_('Funding Transaction')) + self.window.show_transaction(tx, tx_desc=_('Transaction')) - def __init__(self, window: 'ElectrumWindow', chan_id: bytes): - super().__init__(window) - - # initialize instance fields - self.window = window - self.wallet = window.wallet - chan = self.chan = window.wallet.lnworker.channels[chan_id] - self.format_msat = lambda msat: window.format_amount_and_units(msat / 1000) - self.format_sat = lambda sat: window.format_amount_and_units(sat) - - # register callbacks for updating - self.register_callbacks() - - # set attributes of QDialog - self.setWindowTitle(_('Channel Details')) - self.setMinimumSize(800, 400) - - # add layouts - vbox = QtWidgets.QVBoxLayout(self) + def get_common_form(self, chan): form = QtWidgets.QFormLayout(None) remote_id_e = ShowQRLineEdit(chan.node_id.hex(), self.window.config, title=_("Remote Node ID")) form.addRow(QLabel(_('Remote Node') + ':'), remote_id_e) channel_id_e = ShowQRLineEdit(chan.channel_id.hex(), self.window.config, title=_("Channel ID")) form.addRow(QLabel(_('Channel ID') + ':'), channel_id_e) - funding_label_text = f'{chan.funding_outpoint.txid}:{chan.funding_outpoint.output_index}' - form.addRow(QLabel(_('Funding Outpoint') + ':'), LinkedLabel(funding_label_text, self.show_tx)) + form.addRow(QLabel(_('Short Channel ID') + ':'), QLabel(str(chan.short_channel_id))) form.addRow(QLabel(_('State') + ':'), SelectableLabel(chan.get_state_for_GUI())) + self.capacity = self.format_sat(chan.get_capacity()) form.addRow(QLabel(_('Capacity') + ':'), SelectableLabel(self.capacity)) - form.addRow(QLabel(_('Channel type:')), SelectableLabel(chan.storage['channel_type'].name_minimal)) - initiator = 'Local' if chan.constraints.is_initiator else 'Remote' - form.addRow(QLabel(_('Initiator:')), SelectableLabel(initiator)) - vbox.addLayout(form) + if not chan.is_backup(): + form.addRow(QLabel(_('Channel type:')), SelectableLabel(chan.storage['channel_type'].name_minimal)) + initiator = 'Local' if chan.constraints.is_initiator else 'Remote' + form.addRow(QLabel(_('Initiator:')), SelectableLabel(initiator)) + else: + form.addRow(QLabel("Backup Type"), QLabel("imported" if self.chan.is_imported else "on-chain")) + funding_txid = chan.funding_outpoint.txid + funding_label_text = f'{funding_txid}:{chan.funding_outpoint.output_index}' + form.addRow(QLabel(_('Funding Outpoint') + ':'), LinkedLabel(funding_label_text, self.show_tx)) + if chan.is_closed(): + item = chan.get_closing_height() + if item: + closing_txid, closing_height, timestamp = item + closing_label_text = f'{closing_txid}' + form.addRow(QLabel(_('Closing Transaction') + ':'), LinkedLabel(closing_label_text, self.show_tx)) + return form + + def get_hbox_stats(self, chan): hbox_stats = QHBoxLayout() form_layout_left = QtWidgets.QFormLayout(None) form_layout_right = QtWidgets.QFormLayout(None) - self.local_balance_label = SelectableLabel() - self.remote_balance_label = SelectableLabel() form_layout_left.addRow(_('Local balance') + ':', self.local_balance_label) form_layout_right.addRow(_('Remote balance') + ':', self.remote_balance_label) - self.can_send_label = SelectableLabel() - self.can_receive_label = SelectableLabel() form_layout_left.addRow(_('Can send') + ':', self.can_send_label) form_layout_right.addRow(_('Can receive') + ':', self.can_receive_label) local_reserve_label = SelectableLabel("{}".format( @@ -209,9 +247,9 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener): hbox_stats.addWidget(line_separator) # channel stats right column hbox_stats.addLayout(form_layout_right, 50) - vbox.addLayout(hbox_stats) - # add htlc tree view to vbox (wouldn't scale correctly in QFormLayout) - vbox.addWidget(QLabel(_('Payments (HTLCs):'))) + return hbox_stats + + def create_htlc_list(self, chan): w = QtWidgets.QTreeView(self) htlc_dict = chan.get_payments() htlc_list = [] @@ -220,10 +258,7 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener): htlc_list.append(htlc_with_status) w.setModel(self.make_model(htlc_list)) w.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) - vbox.addWidget(w) - vbox.addLayout(Buttons(CloseButton(self))) - # initialize sent/received fields - self.update() + return w def closeEvent(self, event): self.unregister_callbacks() diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py index 82367a8d4..6340645c3 100644 --- a/electrum/gui/qt/channels_list.py +++ b/electrum/gui/qt/channels_list.py @@ -233,19 +233,8 @@ class ChannelsList(MyTreeView): if not item: return channel_id = idx.sibling(idx.row(), self.Columns.NODE_ALIAS).data(ROLE_CHANNEL_ID) - chan = self.lnworker.channel_backups.get(channel_id) or self.lnworker.channels[channel_id] - if not chan.is_backup(): - menu.addAction(_("Details..."), lambda: self.parent.show_channel(channel_id)) - funding_tx = self.parent.wallet.db.get_transaction(chan.funding_outpoint.txid) - if funding_tx: - menu.addAction(_("View funding transaction"), lambda: self.parent.show_transaction(funding_tx)) - if chan.is_closed(): - item = chan.get_closing_height() - if item: - txid, height, timestamp = item - closing_tx = self.parent.wallet.db.get_transaction(txid) - if closing_tx: - menu.addAction(_("View closing transaction"), lambda: self.parent.show_transaction(closing_tx)) + chan = self.lnworker.channels.get(channel_id) or self.lnworker.channel_backups[channel_id] + menu.addAction(_("Details..."), lambda: self.parent.show_channel_details(chan)) menu.addSeparator() cc = self.add_copy_menu(menu, idx) cc.addAction(_("Node ID"), lambda: self.place_text_on_clipboard( diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index afc666015..a3e718af5 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1059,9 +1059,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): d = address_dialog.AddressDialog(self, addr) d.exec_() - def show_channel(self, channel_id): - from . import channel_details - channel_details.ChannelDetailsDialog(self, channel_id).show() + def show_channel_details(self, chan): + from .channel_details import ChannelDetailsDialog + ChannelDetailsDialog(self, chan).show() def show_transaction(self, tx, *, tx_desc=None): '''tx_desc is set only for txs created in the Send tab'''