commit feb47b0a6ff7c5f0dd4ab09ccdb1443133198ff9
parent b3a3267ede6a9ea97187c4b2d730b9d9ab1eec70
Author: ThomasV <thomasv@electrum.org>
Date: Tue, 18 Feb 2020 12:34:09 +0100
Add lightning tx dialog (qt and kivy)
Diffstat:
7 files changed, 222 insertions(+), 1 deletion(-)
diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
@@ -1040,6 +1040,11 @@ class ElectrumWindow(App):
d = TxDialog(self, tx)
d.open()
+ def lightning_tx_dialog(self, tx):
+ from .uix.dialogs.lightning_tx_dialog import LightningTxDialog
+ d = LightningTxDialog(self, tx)
+ d.open()
+
def sign_tx(self, *args):
threading.Thread(target=self._sign_tx, args=args).start()
diff --git a/electrum/gui/kivy/uix/dialogs/lightning_tx_dialog.py b/electrum/gui/kivy/uix/dialogs/lightning_tx_dialog.py
@@ -0,0 +1,111 @@
+import copy
+from datetime import datetime
+from decimal import Decimal
+from typing import NamedTuple, Callable, TYPE_CHECKING
+
+from kivy.app import App
+from kivy.factory import Factory
+from kivy.properties import ObjectProperty
+from kivy.lang import Builder
+from kivy.clock import Clock
+from kivy.uix.label import Label
+from kivy.uix.dropdown import DropDown
+from kivy.uix.button import Button
+
+from electrum.gui.kivy.i18n import _
+
+
+if TYPE_CHECKING:
+ from ...main_window import ElectrumWindow
+
+
+Builder.load_string('''
+
+<LightningTxDialog>
+ id: popup
+ title: _('Lightning Payment')
+ preimage: ''
+ is_sent: False
+ amount_str: ''
+ fee_str: ''
+ date_str: ''
+ payment_hash: ''
+ description: ''
+ BoxLayout:
+ orientation: 'vertical'
+ ScrollView:
+ scroll_type: ['bars', 'content']
+ bar_width: '25dp'
+ GridLayout:
+ height: self.minimum_height
+ size_hint_y: None
+ cols: 1
+ spacing: '10dp'
+ padding: '10dp'
+ GridLayout:
+ height: self.minimum_height
+ size_hint_y: None
+ cols: 1
+ spacing: '10dp'
+ BoxLabel:
+ text: _('Description') if root.description else ''
+ value: root.description
+ BoxLabel:
+ text: _('Date')
+ value: root.date_str
+ BoxLabel:
+ text: _('Amount sent') if root.is_sent else _('Amount received')
+ value: root.amount_str
+ BoxLabel:
+ text: _('Transaction fee') if root.fee_str else ''
+ value: root.fee_str
+ TopLabel:
+ text: _('Payment hash') + ':'
+ TxHashLabel:
+ data: root.payment_hash
+ name: _('Payment hash')
+ TopLabel:
+ text: _('Preimage')
+ TxHashLabel:
+ data: root.preimage
+ name: _('Preimage')
+
+ Widget:
+ size_hint: 1, 0.1
+
+ BoxLayout:
+ size_hint: 1, None
+ height: '48dp'
+ Widget
+ Button:
+ size_hint: 0.5, None
+ height: '48dp'
+ text: _('Close')
+ on_release: root.dismiss()
+''')
+
+
+class ActionButtonOption(NamedTuple):
+ text: str
+ func: Callable
+ enabled: bool
+
+
+class LightningTxDialog(Factory.Popup):
+
+ def __init__(self, app, tx_item):
+ Factory.Popup.__init__(self)
+ self.app = app # type: ElectrumWindow
+ self.wallet = self.app.wallet
+ self._action_button_fn = lambda btn: None
+ self.is_sent = bool(tx_item['direction'] is 'sent')
+ self.description = tx_item['label']
+ self.timestamp = tx_item['timestamp']
+ self.date_str = datetime.fromtimestamp(self.timestamp).isoformat(' ')[:-3]
+ self.amount = Decimal(tx_item['amount_msat']) /1000
+ self.payment_hash = tx_item['payment_hash']
+ self.preimage = tx_item['preimage']
+ format_amount = self.app.format_amount_and_units
+ self.amount_str = format_amount(self.amount)
+ if self.is_sent:
+ self.fee_str = format_amount(Decimal(tx_item['fee_msat']) / 1000)
diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
@@ -126,6 +126,10 @@ class HistoryScreen(CScreen):
def show_item(self, obj):
key = obj.key
+ tx_item = self.history.get(key)
+ if obj.is_lightning:
+ self.app.lightning_tx_dialog(tx_item)
+ return
tx = self.app.wallet.db.get_transaction(key)
if not tx:
return
@@ -156,6 +160,7 @@ class HistoryScreen(CScreen):
fee_text = '' if fee is None else 'fee: %d sat'%fee
ri = {}
ri['screen'] = self
+ ri['is_lightning'] = is_lightning
ri['key'] = key
ri['icon'] = icon
ri['date'] = status_str
@@ -173,7 +178,8 @@ class HistoryScreen(CScreen):
wallet = self.app.wallet
if wallet is None:
return
- history = reversed(wallet.get_full_history(self.app.fx).values())
+ self.history = wallet.get_full_history(self.app.fx)
+ history = reversed(self.history.values())
history_card = self.screen.ids.history_container
history_card.data = [self.get_card(item) for item in history]
diff --git a/electrum/gui/qt/history_list.py b/electrum/gui/qt/history_list.py
@@ -580,6 +580,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
def show_transaction(self, tx_item):
if tx_item.get('lightning'):
+ self.parent.show_lightning_transaction(tx_item)
return
tx_hash = tx_item['txid']
tx = self.wallet.db.get_transaction(tx_hash)
@@ -608,6 +609,11 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
return
tx_item = self.hm.transactions.value_from_pos(idx.row())
if tx_item.get('lightning'):
+ menu = QMenu()
+ #tx_hash = tx_item['txid']
+ #menu.addAction(_("Copy Transaction ID"), lambda: self.place_text_on_clipboard(tx_hash, title="TXID"))
+ menu.addAction(_("Details"), lambda: self.parent.show_lightning_transaction(tx_item))
+ menu.exec_(self.viewport().mapToGlobal(position))
return
tx_hash = tx_item['txid']
tx = self.wallet.db.get_transaction(tx_hash)
diff --git a/electrum/gui/qt/lightning_tx_dialog.py b/electrum/gui/qt/lightning_tx_dialog.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+#
+# Electrum - lightweight Bitcoin client
+# Copyright (C) 2020 The Electrum Developers
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from typing import TYPE_CHECKING
+from decimal import Decimal
+import datetime
+from PyQt5.QtWidgets import QVBoxLayout, QLabel, QGridLayout
+
+from electrum.i18n import _
+from .util import WindowModalDialog, ButtonsLineEdit, ColorScheme, Buttons, CloseButton
+
+if TYPE_CHECKING:
+ from .main_window import ElectrumWindow
+
+
+
+class LightningTxDialog(WindowModalDialog):
+
+ def __init__(self, parent: 'ElectrumWindow', tx_item: dict):
+ WindowModalDialog.__init__(self, parent, _("Lightning Payment"))
+ self.parent = parent
+ self.is_sent = bool(tx_item['direction'] is 'sent')
+ self.label = tx_item['label']
+ self.timestamp = tx_item['timestamp']
+ self.amount = Decimal(tx_item['amount_msat']) /1000
+ self.payment_hash = tx_item['payment_hash']
+ self.preimage = tx_item['preimage']
+ self.setMinimumWidth(700)
+ vbox = QVBoxLayout()
+ self.setLayout(vbox)
+
+ vbox.addWidget(QLabel(_("Amount:") + self.parent.format_amount_and_units(self.amount)))
+ if self.is_sent:
+ fee = Decimal(tx_item['fee_msat']) / 1000
+ vbox.addWidget(QLabel(_("Fee:") + self.parent.format_amount_and_units(fee)))
+ time_str = datetime.datetime.fromtimestamp(self.timestamp).isoformat(' ')[:-3]
+ vbox.addWidget(QLabel(_("Date:") + time_str))
+
+ qr_icon = "qrcode_white.png" if ColorScheme.dark_scheme else "qrcode.png"
+
+ vbox.addWidget(QLabel(_("Payment hash:")))
+ self.hash_e = ButtonsLineEdit(self.payment_hash)
+ self.hash_e.addCopyButton(self.parent.app)
+ self.hash_e.addButton(qr_icon, self.show_qr, _("Show QR Code"))
+ self.hash_e.setReadOnly(True)
+ vbox.addWidget(self.hash_e)
+
+ vbox.addWidget(QLabel(_("Preimage:")))
+ self.preimage_e = ButtonsLineEdit(self.preimage)
+ self.preimage_e.addCopyButton(self.parent.app)
+ self.preimage_e.addButton(qr_icon, self.show_qr, _("Show QR Code"))
+ self.preimage_e.setReadOnly(True)
+ vbox.addWidget(self.preimage_e)
+
+ vbox.addLayout(Buttons(CloseButton(self)))
+
+ def show_qr(self):
+ text = self.address
+ try:
+ self.parent.show_qrcode(text, '', parent=self)
+ except Exception as e:
+ self.show_message(repr(e))
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
@@ -956,6 +956,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
'''tx_desc is set only for txs created in the Send tab'''
show_transaction(tx, parent=self, desc=tx_desc)
+ def show_lightning_transaction(self, tx_item):
+ from .lightning_tx_dialog import LightningTxDialog
+ d = LightningTxDialog(self, tx_item)
+ d.show()
+
def create_receive_tab(self):
# A 4-column grid layout. All the stretch is in the last column.
# The exchange rate plugin adds a fiat widget in column 2
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
@@ -502,6 +502,9 @@ class LNWallet(LNWorker):
timestamp = min([htlc.timestamp for chan_id, htlc, _direction in plist])
fee_msat = None # fixme
+ payment_hash = bytes.fromhex(key)
+ preimage = self.get_preimage(payment_hash).hex()
+
item = {
'type': 'payment',
'label': label,
@@ -511,6 +514,7 @@ class LNWallet(LNWorker):
'amount_msat': amount_msat,
'fee_msat': fee_msat,
'payment_hash': key,
+ 'preimage': preimage,
}
out.append(item)
# add funding events