URI: 
       tqt send tab: handle invalid ln invoice; and ln invoice with ln disabled - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 8dabdf8bfb3abea550ed0e15c7695ad992d936fa
   DIR parent 1773bd6cd6b9aaa693c6c1cb865f88fc3597e09f
  HTML 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:
         M electrum/gui/qt/main_window.py      |      65 ++++++++++++++++++++-----------
         M electrum/gui/qt/paytoedit.py        |      26 ++++++++++++++++++++------
         M electrum/lnaddr.py                  |       8 ++++++--
       
       3 files changed, 68 insertions(+), 31 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -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():
       t@@ -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:
       t@@ -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):
       t@@ -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):
       t@@ -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':
   DIR diff --git a/electrum/gui/qt/paytoedit.py b/electrum/gui/qt/paytoedit.py
       t@@ -25,6 +25,7 @@
        
        import re
        from decimal import Decimal
       +from typing import NamedTuple, Sequence
        
        from PyQt5.QtGui import QFontMetrics
        
       t@@ -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
       t@@ -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):
       t@@ -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):
       t@@ -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:"):
       t@@ -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)
       t@@ -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 == '!':
       t@@ -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):
   DIR diff --git a/electrum/lnaddr.py b/electrum/lnaddr.py
       t@@ -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")