Browse Source

Qt: improve channel details window

hard-fail-on-bad-server-string
ThomasV 5 years ago
parent
commit
5bac2fea98
  1. 4
      electrum/gui/kivy/uix/dialogs/lightning_channels.py
  2. 87
      electrum/gui/qt/channel_details.py
  3. 2
      electrum/gui/qt/channels_list.py
  4. 10
      electrum/lnchannel.py
  5. 12
      electrum/lnworker.py

4
electrum/gui/kivy/uix/dialogs/lightning_channels.py

@ -208,7 +208,7 @@ class ChannelDetailsPopup(Popup):
self.funding_txid = chan.funding_outpoint.txid self.funding_txid = chan.funding_outpoint.txid
self.short_id = format_short_channel_id(chan.short_channel_id) self.short_id = format_short_channel_id(chan.short_channel_id)
self.capacity = self.app.format_amount_and_units(chan.constraints.capacity) 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.local_ctn = chan.get_latest_ctn(LOCAL)
self.remote_ctn = chan.get_latest_ctn(REMOTE) self.remote_ctn = chan.get_latest_ctn(REMOTE)
self.local_csv = chan.config[LOCAL].to_self_delay self.local_csv = chan.config[LOCAL].to_self_delay
@ -297,7 +297,7 @@ class LightningChannelsDialog(Factory.Popup):
def update_item(self, item): def update_item(self, item):
chan = item._chan 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) item.short_channel_id = format_short_channel_id(chan.short_channel_id)
l, r = self.format_fields(chan) l, r = self.format_fields(chan)
item.local_balance = _('Local') + ':' + l item.local_balance = _('Local') + ':' + l

87
electrum/gui/qt/channel_details.py

@ -3,15 +3,16 @@ from typing import TYPE_CHECKING
import PyQt5.QtGui as QtGui import PyQt5.QtGui as QtGui
import PyQt5.QtWidgets as QtWidgets import PyQt5.QtWidgets as QtWidgets
import PyQt5.QtCore as QtCore import PyQt5.QtCore as QtCore
from PyQt5.QtWidgets import QLabel, QLineEdit
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import bh2u, format_time from electrum.util import bh2u, format_time
from electrum.lnutil import format_short_channel_id, LOCAL, REMOTE, UpdateAddHtlc, Direction 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.lnaddr import LnAddr, lndecode
from electrum.bitcoin import COIN from electrum.bitcoin import COIN
from .util import Buttons, CloseButton from .util import Buttons, CloseButton, ButtonsLineEdit
if TYPE_CHECKING: if TYPE_CHECKING:
from .main_window import ElectrumWindow from .main_window import ElectrumWindow
@ -34,7 +35,7 @@ class LinkedLabel(QtWidgets.QLabel):
class ChannelDetailsDialog(QtWidgets.QDialog): class ChannelDetailsDialog(QtWidgets.QDialog):
def make_htlc_item(self, i: UpdateAddHtlc, direction: Direction) -> HTLCItem: 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 = 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(_('CLTV expiry')),HTLCItem(str(i.cltv_expiry))])
it.appendRow([HTLCItem(_('Payment hash')),HTLCItem(bh2u(i.payment_hash))]) it.appendRow([HTLCItem(_('Payment hash')),HTLCItem(bh2u(i.payment_hash))])
return it return it
@ -76,7 +77,14 @@ class ChannelDetailsDialog(QtWidgets.QDialog):
dest_mapping[payment_hash] = len(dest_mapping) dest_mapping[payment_hash] = len(dest_mapping)
ln_payment_completed = QtCore.pyqtSignal(str, bytes, bytes) ln_payment_completed = QtCore.pyqtSignal(str, bytes, bytes)
ln_payment_failed = QtCore.pyqtSignal(str, bytes, bytes)
htlc_added = QtCore.pyqtSignal(str, UpdateAddHtlc, LnAddr, Direction) 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) @QtCore.pyqtSlot(str, UpdateAddHtlc, LnAddr, Direction)
def do_htlc_added(self, evtname, htlc, 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: if chan_id != self.chan.channel_id:
return return
self.move('inflight', 'settled', payment_hash) 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): def update(self):
self.sent_label.setText(str(self.chan.total_msat(Direction.SENT))) self.can_send_label.setText(self.format_msat(self.chan.available_to_spend(LOCAL)))
self.received_label.setText(str(self.chan.total_msat(Direction.RECEIVED))) 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) @QtCore.pyqtSlot(str)
def show_tx(self, link_text: str): def show_tx(self, link_text: str):
@ -106,15 +123,19 @@ class ChannelDetailsDialog(QtWidgets.QDialog):
# initialize instance fields # initialize instance fields
self.window = window self.window = window
chan = self.chan = window.wallet.lnworker.channels[chan_id] 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 # connect signals with slots
self.ln_payment_completed.connect(self.do_ln_payment_completed) 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) self.htlc_added.connect(self.do_htlc_added)
# register callbacks for updating # register callbacks for updating
window.network.register_callback(self.ln_payment_completed.emit, ['ln_payment_completed']) 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.htlc_added.emit, ['htlc_added'])
window.network.register_callback(self.state_changed.emit, ['channel'])
# set attributes of QDialog # set attributes of QDialog
self.setWindowTitle(_('Channel Details')) self.setWindowTitle(_('Channel Details'))
@ -122,32 +143,44 @@ class ChannelDetailsDialog(QtWidgets.QDialog):
# add layouts # add layouts
vbox = QtWidgets.QVBoxLayout(self) vbox = QtWidgets.QVBoxLayout(self)
form_layout = QtWidgets.QFormLayout(None) vbox.addWidget(QLabel(_('Remote Node ID:')))
vbox.addLayout(form_layout) 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 # add form content
form_layout.addRow(_('Node ID:'), SelectableLabel(bh2u(chan.node_id))) form_layout.addRow(_('Channel ID:'), SelectableLabel(chan.get_id_for_log()))
form_layout.addRow(_('Channel ID:'), SelectableLabel(bh2u(chan.channel_id))) form_layout.addRow(_('State:'), SelectableLabel(chan.get_state_for_GUI()))
funding_label_text = f'<a href=click_destination>{chan.funding_outpoint.txid}</a>:{chan.funding_outpoint.output_index}' self.initiator = 'Local' if chan.constraints.is_initiator else 'Remote'
form_layout.addRow(_('Funding Outpoint:'), LinkedLabel(funding_label_text, self.show_tx)) form_layout.addRow(_('Initiator:'), SelectableLabel(self.initiator))
form_layout.addRow(_('Short Channel ID:'), SelectableLabel(format_short_channel_id(chan.short_channel_id))) 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() self.received_label = SelectableLabel()
form_layout.addRow(_('Received (mSAT):'), self.received_label) form_layout.addRow(_('Received:'), self.received_label)
self.sent_label = SelectableLabel() self.sent_label = SelectableLabel()
form_layout.addRow(_('Sent (mSAT):'), self.sent_label) form_layout.addRow(_('Sent:'), self.sent_label)
self.htlc_minimum_msat = SelectableLabel(str(chan.config[REMOTE].htlc_minimum_msat)) #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) #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)) #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) #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)) #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(_('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)) 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) form_layout.addRow(_('Remote dust limit:'), self.dust_limit)
self.reserve = SelectableLabel(self.window.format_amount_and_units(chan.config[REMOTE].reserve_sat)) self.remote_reserve = self.window.format_amount_and_units(chan.config[REMOTE].reserve_sat)
form_layout.addRow(_('Remote channel reserve:'), self.reserve) 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) # 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) w = QtWidgets.QTreeView(self)
htlc_dict = chan.get_payments() htlc_dict = chan.get_payments()
w.setModel(self.make_model(htlc_dict)) w.setModel(self.make_model(htlc_dict))
@ -155,4 +188,4 @@ class ChannelDetailsDialog(QtWidgets.QDialog):
vbox.addWidget(w) vbox.addWidget(w)
vbox.addLayout(Buttons(CloseButton(self))) vbox.addLayout(Buttons(CloseButton(self)))
# initialize sent/received fields # initialize sent/received fields
self.update_sent_received() self.update()

2
electrum/gui/qt/channels_list.py

@ -64,7 +64,7 @@ class ChannelsList(MyTreeView):
if bal_other != bal_minus_htlcs_other: if bal_other != bal_minus_htlcs_other:
label += ' (+' + self.parent.format_amount(bal_other - bal_minus_htlcs_other) + ')' label += ' (+' + self.parent.format_amount(bal_other - bal_minus_htlcs_other) + ')'
labels[subject] = label labels[subject] = label
status = self.lnworker.get_channel_status(chan) status = chan.get_state_for_GUI()
closed = chan.is_closed() closed = chan.is_closed()
if self.parent.network.is_lightning_running(): if self.parent.network.is_lightning_running():
node_info = self.lnworker.channel_db.get_node_info_for_node_id(chan.node_id) node_info = self.lnworker.channel_db.get_node_info_for_node_id(chan.node_id)

10
electrum/lnchannel.py

@ -331,6 +331,16 @@ class Channel(Logger):
def get_state(self): def get_state(self):
return self._state 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): def is_open(self):
return self.get_state() == channel_states.OPEN return self.get_state() == channel_states.OPEN

12
electrum/lnworker.py

@ -510,17 +510,6 @@ class LNWallet(LNWorker):
self.network.trigger_callback('channel', chan) self.network.trigger_callback('channel', chan)
super().peer_closed(peer) 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): def get_settled_payments(self):
# return one item per payment_hash # return one item per payment_hash
# note: with AMP we will have several channels per payment # 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') chan.logger.info('received unexpected payment_failed, probably from previous session')
self.network.trigger_callback('invoice_status', key) self.network.trigger_callback('invoice_status', key)
self.network.trigger_callback('payment_failed', 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): def payment_sent(self, chan, payment_hash: bytes):
self.set_payment_status(payment_hash, PR_PAID) self.set_payment_status(payment_hash, PR_PAID)

Loading…
Cancel
Save