commit 8dabdf8bfb3abea550ed0e15c7695ad992d936fa
parent 1773bd6cd6b9aaa693c6c1cb865f88fc3597e09f
Author: SomberNight <somber.night@protonmail.com>
Date: Tue, 1 Oct 2019 19:07:27 +0200
qt send tab: handle invalid ln invoice; and ln invoice with ln disabled
fixes #5639
fixes #5662
Diffstat:
3 files changed, 68 insertions(+), 31 deletions(-)
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
@@ -1629,10 +1629,31 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
outputs = self.payto_e.get_outputs(self.max_button.isChecked())
return outputs
- def check_send_tab_outputs_and_show_errors(self, outputs) -> bool:
+ def check_send_tab_onchain_outputs_and_show_errors(self, outputs) -> bool:
"""Returns whether there are errors with outputs.
Also shows error dialog to user if so.
"""
+ if not outputs:
+ self.show_error(_('No outputs'))
+ return True
+
+ for o in outputs:
+ if o.address is None:
+ self.show_error(_('Bitcoin Address is None'))
+ return True
+ if o.type == TYPE_ADDRESS and not bitcoin.is_address(o.address):
+ self.show_error(_('Invalid Bitcoin Address'))
+ return True
+ if o.value is None:
+ self.show_error(_('Invalid Amount'))
+ return True
+
+ return False # no errors
+
+ def check_send_tab_payto_line_and_show_errors(self) -> bool:
+ """Returns whether there are errors.
+ Also shows error dialog to user if so.
+ """
pr = self.payment_request
if pr:
if pr.has_expired():
@@ -1642,7 +1663,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
if not pr:
errors = self.payto_e.get_errors()
if errors:
- self.show_warning(_("Invalid Lines found:") + "\n\n" + '\n'.join([ _("Line #") + str(x[0]+1) + ": " + x[1] for x in errors]))
+ self.show_warning(_("Invalid Lines found:") + "\n\n" +
+ '\n'.join([_("Line #") + f"{err.idx+1}: {err.line_content[:40]}... ({repr(err.exc)})"
+ for err in errors]))
return True
if self.payto_e.is_alias and self.payto_e.validated is False:
@@ -1653,21 +1676,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
if not self.question(msg):
return True
- if not outputs:
- self.show_error(_('No outputs'))
- return True
-
- for o in outputs:
- if o.address is None:
- self.show_error(_('Bitcoin Address is None'))
- return True
- if o.type == TYPE_ADDRESS and not bitcoin.is_address(o.address):
- self.show_error(_('Invalid Bitcoin Address'))
- return True
- if o.value is None:
- self.show_error(_('Invalid Amount'))
- return True
-
return False # no errors
def pay_lightning_invoice(self, invoice):
@@ -1694,14 +1702,21 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self.show_error(_('Error') + '\n' + str(e))
def read_invoice(self):
- message = self.message_e.text()
- amount = self.amount_e.get_amount()
+ if self.check_send_tab_payto_line_and_show_errors():
+ return
if not self.is_onchain:
- return self.wallet.lnworker.parse_bech32_invoice(self.payto_e.lightning_invoice)
+ invoice = self.payto_e.lightning_invoice
+ if not invoice:
+ return
+ if not self.wallet.lnworker:
+ self.show_error(_('Lightning is disabled'))
+ return
+ return self.wallet.lnworker.parse_bech32_invoice(invoice)
else:
outputs = self.read_outputs()
- if self.check_send_tab_outputs_and_show_errors(outputs):
+ if self.check_send_tab_onchain_outputs_and_show_errors(outputs):
return
+ message = self.message_e.text()
return self.wallet.create_invoice(outputs, message, self.payment_request, self.payto_URI)
def do_save_invoice(self):
@@ -1968,8 +1983,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self.payment_request_error_signal.emit()
def parse_lightning_invoice(self, invoice):
- from electrum.lnaddr import lndecode
- lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
+ """Parse ln invoice, and prepare the send tab for it."""
+ from electrum.lnaddr import lndecode, LnDecodeException
+ try:
+ lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
+ except Exception as e:
+ raise LnDecodeException(e) from e
pubkey = bh2u(lnaddr.pubkey.serialize())
for k,v in lnaddr.tags:
if k == 'd':
diff --git a/electrum/gui/qt/paytoedit.py b/electrum/gui/qt/paytoedit.py
@@ -25,6 +25,7 @@
import re
from decimal import Decimal
+from typing import NamedTuple, Sequence
from PyQt5.QtGui import QFontMetrics
@@ -33,6 +34,7 @@ from electrum.util import bfh
from electrum.transaction import TxOutput, push_script
from electrum.bitcoin import opcodes
from electrum.logging import Logger
+from electrum.lnaddr import LnDecodeException
from .qrtextedit import ScanQRTextEdit
from .completion_text_edit import CompletionTextEdit
@@ -44,6 +46,12 @@ frozen_style = "QWidget {border:none;}"
normal_style = "QPlainTextEdit { }"
+class PayToLineError(NamedTuple):
+ idx: int # index of line
+ line_content: str
+ exc: Exception
+
+
class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
def __init__(self, win):
@@ -58,11 +66,12 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.c = None
self.textChanged.connect(self.check_text)
self.outputs = []
- self.errors = []
+ self.errors = [] # type: Sequence[PayToLineError]
self.is_pr = False
self.is_alias = False
self.update_size()
self.payto_address = None
+ self.lightning_invoice = None
self.previous_payto = ''
def setFrozen(self, b):
@@ -125,6 +134,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
outputs = []
total = 0
self.payto_address = None
+ self.lightning_invoice = None
if len(lines) == 1:
data = lines[0]
if data.startswith("bitcoin:"):
@@ -134,8 +144,12 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
if lower.startswith("lightning:ln"):
lower = lower[10:]
if lower.startswith("ln"):
- self.win.parse_lightning_invoice(lower)
- self.lightning_invoice = lower
+ try:
+ self.win.parse_lightning_invoice(lower)
+ except LnDecodeException as e:
+ self.errors.append(PayToLineError(idx=0, line_content=data, exc=e))
+ else:
+ self.lightning_invoice = lower
return
try:
self.payto_address = self.parse_output(data)
@@ -150,8 +164,8 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
for i, line in enumerate(lines):
try:
output = self.parse_address_and_amount(line)
- except:
- self.errors.append((i, line.strip()))
+ except Exception as e:
+ self.errors.append(PayToLineError(idx=i, line_content=line.strip(), exc=e))
continue
outputs.append(output)
if output.value == '!':
@@ -171,7 +185,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.amount_edit.setAmount(total if outputs else None)
self.win.lock_amount(total or len(lines)>1)
- def get_errors(self):
+ def get_errors(self) -> Sequence[PayToLineError]:
return self.errors
def get_recipient(self):
diff --git a/electrum/lnaddr.py b/electrum/lnaddr.py
@@ -276,10 +276,14 @@ class LnAddr(object):
now = time.time()
return now > self.get_expiry() + self.date
-def lndecode(a, verbose=False, expected_hrp=None):
+
+class LnDecodeException(Exception): pass
+
+
+def lndecode(invoice: str, *, verbose=False, expected_hrp=None) -> LnAddr:
if expected_hrp is None:
expected_hrp = constants.net.SEGWIT_HRP
- hrp, data = bech32_decode(a, ignore_long_length=True)
+ hrp, data = bech32_decode(invoice, ignore_long_length=True)
if not hrp:
raise ValueError("Bad bech32 checksum")