electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 5bac2fea986d27d0e2aa26fe29987e7d24233f88
parent ffa3760a179f006a7758309b16cb4bac9bb77be1
Author: ThomasV <thomasv@electrum.org>
Date:   Sat,  7 Mar 2020 16:37:21 +0100

Qt: improve channel details window

Diffstat:
Melectrum/gui/kivy/uix/dialogs/lightning_channels.py | 4++--
Melectrum/gui/qt/channel_details.py | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Melectrum/gui/qt/channels_list.py | 2+-
Melectrum/lnchannel.py | 10++++++++++
Melectrum/lnworker.py | 12+-----------
5 files changed, 74 insertions(+), 41 deletions(-)

diff --git a/electrum/gui/kivy/uix/dialogs/lightning_channels.py b/electrum/gui/kivy/uix/dialogs/lightning_channels.py @@ -208,7 +208,7 @@ class ChannelDetailsPopup(Popup): self.funding_txid = chan.funding_outpoint.txid self.short_id = format_short_channel_id(chan.short_channel_id) self.capacity = self.app.format_amount_and_units(chan.constraints.capacity) - self.state = self.app.wallet.lnworker.get_channel_status(chan) + self.state = chan.get_state_for_GUI() self.local_ctn = chan.get_latest_ctn(LOCAL) self.remote_ctn = chan.get_latest_ctn(REMOTE) self.local_csv = chan.config[LOCAL].to_self_delay @@ -297,7 +297,7 @@ class LightningChannelsDialog(Factory.Popup): def update_item(self, item): chan = item._chan - item.status = self.app.wallet.lnworker.get_channel_status(chan) + item.status = chan.get_state_for_GUI() item.short_channel_id = format_short_channel_id(chan.short_channel_id) l, r = self.format_fields(chan) item.local_balance = _('Local') + ':' + l diff --git a/electrum/gui/qt/channel_details.py b/electrum/gui/qt/channel_details.py @@ -3,15 +3,16 @@ from typing import TYPE_CHECKING import PyQt5.QtGui as QtGui import PyQt5.QtWidgets as QtWidgets import PyQt5.QtCore as QtCore +from PyQt5.QtWidgets import QLabel, QLineEdit from electrum.i18n import _ from electrum.util import bh2u, format_time from electrum.lnutil import format_short_channel_id, LOCAL, REMOTE, UpdateAddHtlc, Direction -from electrum.lnchannel import htlcsum +from electrum.lnchannel import htlcsum, Channel from electrum.lnaddr import LnAddr, lndecode from electrum.bitcoin import COIN -from .util import Buttons, CloseButton +from .util import Buttons, CloseButton, ButtonsLineEdit if TYPE_CHECKING: from .main_window import ElectrumWindow @@ -34,7 +35,7 @@ class LinkedLabel(QtWidgets.QLabel): class ChannelDetailsDialog(QtWidgets.QDialog): 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(i.amount_msat))]) + it.appendRow([HTLCItem(_('Amount')),HTLCItem(self.format_msat(i.amount_msat))]) it.appendRow([HTLCItem(_('CLTV expiry')),HTLCItem(str(i.cltv_expiry))]) it.appendRow([HTLCItem(_('Payment hash')),HTLCItem(bh2u(i.payment_hash))]) return it @@ -76,7 +77,14 @@ class ChannelDetailsDialog(QtWidgets.QDialog): dest_mapping[payment_hash] = len(dest_mapping) ln_payment_completed = QtCore.pyqtSignal(str, bytes, bytes) + ln_payment_failed = QtCore.pyqtSignal(str, bytes, bytes) htlc_added = QtCore.pyqtSignal(str, UpdateAddHtlc, LnAddr, Direction) + state_changed = QtCore.pyqtSignal(str, Channel) + + @QtCore.pyqtSlot(str, Channel) + def do_state_changed(self, chan): + if chan == self.chan: + self.update() @QtCore.pyqtSlot(str, UpdateAddHtlc, LnAddr, Direction) def do_htlc_added(self, evtname, htlc, lnaddr, direction): @@ -89,11 +97,20 @@ class ChannelDetailsDialog(QtWidgets.QDialog): if chan_id != self.chan.channel_id: return self.move('inflight', 'settled', payment_hash) - self.update_sent_received() + self.update() + + @QtCore.pyqtSlot(str, bytes, bytes) + def do_ln_payment_failed(self, evtname, payment_hash, chan_id): + if chan_id != self.chan.channel_id: + return + self.move('inflight', 'failed', payment_hash) + self.update() - def update_sent_received(self): - self.sent_label.setText(str(self.chan.total_msat(Direction.SENT))) - self.received_label.setText(str(self.chan.total_msat(Direction.RECEIVED))) + def update(self): + 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))) + self.received_label.setText(self.format_msat(self.chan.total_msat(Direction.RECEIVED))) @QtCore.pyqtSlot(str) def show_tx(self, link_text: str): @@ -106,15 +123,19 @@ class ChannelDetailsDialog(QtWidgets.QDialog): # initialize instance fields self.window = window chan = self.chan = window.wallet.lnworker.channels[chan_id] - self.format = lambda msat: window.format_amount_and_units(msat / 1000) + self.format_msat = lambda msat: window.format_amount_and_units(msat / 1000) # connect signals with slots self.ln_payment_completed.connect(self.do_ln_payment_completed) + self.ln_payment_failed.connect(self.do_ln_payment_failed) + self.state_changed.connect(self.do_state_changed) self.htlc_added.connect(self.do_htlc_added) # register callbacks for updating window.network.register_callback(self.ln_payment_completed.emit, ['ln_payment_completed']) + window.network.register_callback(self.ln_payment_failed.emit, ['ln_payment_failed']) window.network.register_callback(self.htlc_added.emit, ['htlc_added']) + window.network.register_callback(self.state_changed.emit, ['channel']) # set attributes of QDialog self.setWindowTitle(_('Channel Details')) @@ -122,32 +143,44 @@ class ChannelDetailsDialog(QtWidgets.QDialog): # add layouts vbox = QtWidgets.QVBoxLayout(self) - form_layout = QtWidgets.QFormLayout(None) - vbox.addLayout(form_layout) + vbox.addWidget(QLabel(_('Remote Node ID:'))) + remote_id_e = ButtonsLineEdit(bh2u(chan.node_id)) + remote_id_e.addCopyButton(self.window.app) + vbox.addWidget(remote_id_e) + funding_label_text = f'<a href=click_destination>{chan.funding_outpoint.txid}</a>:{chan.funding_outpoint.output_index}' + vbox.addWidget(QLabel(_('Funding Outpoint:'))) + vbox.addWidget(LinkedLabel(funding_label_text, self.show_tx)) + form_layout = QtWidgets.QFormLayout(None) # add form content - form_layout.addRow(_('Node ID:'), SelectableLabel(bh2u(chan.node_id))) - form_layout.addRow(_('Channel ID:'), SelectableLabel(bh2u(chan.channel_id))) - funding_label_text = f'<a href=click_destination>{chan.funding_outpoint.txid}</a>:{chan.funding_outpoint.output_index}' - form_layout.addRow(_('Funding Outpoint:'), LinkedLabel(funding_label_text, self.show_tx)) - form_layout.addRow(_('Short Channel ID:'), SelectableLabel(format_short_channel_id(chan.short_channel_id))) + form_layout.addRow(_('Channel ID:'), SelectableLabel(chan.get_id_for_log())) + form_layout.addRow(_('State:'), SelectableLabel(chan.get_state_for_GUI())) + self.initiator = 'Local' if chan.constraints.is_initiator else 'Remote' + form_layout.addRow(_('Initiator:'), SelectableLabel(self.initiator)) + self.capacity = self.window.format_amount_and_units(chan.constraints.capacity) + form_layout.addRow(_('Capacity:'), SelectableLabel(self.capacity)) + self.can_send_label = SelectableLabel() + self.can_receive_label = SelectableLabel() + form_layout.addRow(_('Can send:'), self.can_send_label) + form_layout.addRow(_('Can receive:'), self.can_receive_label) self.received_label = SelectableLabel() - form_layout.addRow(_('Received (mSAT):'), self.received_label) + form_layout.addRow(_('Received:'), self.received_label) self.sent_label = SelectableLabel() - form_layout.addRow(_('Sent (mSAT):'), self.sent_label) - self.htlc_minimum_msat = SelectableLabel(str(chan.config[REMOTE].htlc_minimum_msat)) - form_layout.addRow(_('Minimum HTLC value accepted by peer (mSAT):'), self.htlc_minimum_msat) - self.max_htlcs = SelectableLabel(str(chan.config[REMOTE].max_accepted_htlcs)) - form_layout.addRow(_('Maximum number of concurrent HTLCs accepted by peer:'), self.max_htlcs) - self.max_htlc_value = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].max_htlc_value_in_flight_msat / 1000)) - form_layout.addRow(_('Maximum value of in-flight HTLCs accepted by peer:'), self.max_htlc_value) + form_layout.addRow(_('Sent:'), self.sent_label) + #self.htlc_minimum_msat = SelectableLabel(str(chan.config[REMOTE].htlc_minimum_msat)) + #form_layout.addRow(_('Minimum HTLC value accepted by peer (mSAT):'), self.htlc_minimum_msat) + #self.max_htlcs = SelectableLabel(str(chan.config[REMOTE].max_accepted_htlcs)) + #form_layout.addRow(_('Maximum number of concurrent HTLCs accepted by peer:'), self.max_htlcs) + #self.max_htlc_value = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].max_htlc_value_in_flight_msat / 1000)) + #form_layout.addRow(_('Maximum value of in-flight HTLCs accepted by peer:'), self.max_htlc_value) self.dust_limit = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].dust_limit_sat)) form_layout.addRow(_('Remote dust limit:'), self.dust_limit) - self.reserve = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].reserve_sat)) - form_layout.addRow(_('Remote channel reserve:'), self.reserve) + self.remote_reserve = self.window.format_amount_and_units(chan.config[REMOTE].reserve_sat) + form_layout.addRow(_('Remote reserve:'), SelectableLabel(self.remote_reserve)) + vbox.addLayout(form_layout) # add htlc tree view to vbox (wouldn't scale correctly in QFormLayout) - form_layout.addRow(_('Payments (HTLCs):'), None) + vbox.addWidget(QLabel(_('Payments (HTLCs):'))) w = QtWidgets.QTreeView(self) htlc_dict = chan.get_payments() w.setModel(self.make_model(htlc_dict)) @@ -155,4 +188,4 @@ class ChannelDetailsDialog(QtWidgets.QDialog): vbox.addWidget(w) vbox.addLayout(Buttons(CloseButton(self))) # initialize sent/received fields - self.update_sent_received() + self.update() diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py @@ -64,7 +64,7 @@ class ChannelsList(MyTreeView): if bal_other != bal_minus_htlcs_other: label += ' (+' + self.parent.format_amount(bal_other - bal_minus_htlcs_other) + ')' labels[subject] = label - status = self.lnworker.get_channel_status(chan) + status = chan.get_state_for_GUI() closed = chan.is_closed() if self.parent.network.is_lightning_running(): node_info = self.lnworker.channel_db.get_node_info_for_node_id(chan.node_id) diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py @@ -331,6 +331,16 @@ class Channel(Logger): def get_state(self): return self._state + def get_state_for_GUI(self): + # status displayed in the GUI + cs = self.get_state() + if self.is_closed(): + return cs.name + ps = self.peer_state + if ps != peer_states.GOOD: + return ps.name + return cs.name + def is_open(self): return self.get_state() == channel_states.OPEN diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -510,17 +510,6 @@ class LNWallet(LNWorker): self.network.trigger_callback('channel', chan) super().peer_closed(peer) - def get_channel_status(self, chan): - # status displayed in the GUI - cs = chan.get_state() - if chan.is_closed(): - return cs.name - peer = self.peers.get(chan.node_id) - ps = chan.peer_state - if ps != peer_states.GOOD: - return ps.name - return cs.name - def get_settled_payments(self): # return one item per payment_hash # note: with AMP we will have several channels per payment @@ -1237,6 +1226,7 @@ class LNWallet(LNWorker): chan.logger.info('received unexpected payment_failed, probably from previous session') self.network.trigger_callback('invoice_status', key) self.network.trigger_callback('payment_failed', key, '') + self.network.trigger_callback('ln_payment_failed', payment_hash, chan.channel_id) def payment_sent(self, chan, payment_hash: bytes): self.set_payment_status(payment_hash, PR_PAID)