URI: 
       tCall wallet.set_paid after onchain broadcast. Check if invoices are expired in util.get_request_status - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit a0ec2690cf0a1c36bc468fbfb5aec1954eff72ae
   DIR parent e35bddcc09ccc9a5f761ccf3c4bce5cdab64f4c6
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Wed, 23 Oct 2019 17:33:46 +0200
       
       Call wallet.set_paid after onchain broadcast. Check if invoices are expired in util.get_request_status
       
       Diffstat:
         M electrum/gui/kivy/main_window.py    |      10 ++++++----
         M electrum/gui/kivy/uix/screens.py    |      31 ++++++++++++++-----------------
         M electrum/gui/qt/invoice_list.py     |       5 ++---
         M electrum/gui/qt/main_window.py      |      21 ++++++++++-----------
         M electrum/gui/qt/request_list.py     |       6 ++----
         M electrum/util.py                    |       4 +++-
         M electrum/wallet.py                  |      12 +++++++-----
       
       7 files changed, 44 insertions(+), 45 deletions(-)
       ---
   DIR diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
       t@@ -1014,15 +1014,17 @@ class ElectrumWindow(App):
                    status, msg = True, tx.txid()
                Clock.schedule_once(lambda dt: on_complete(status, msg))
        
       -    def broadcast(self, tx, pr=None):
       +    def broadcast(self, tx, invoice=None):
                def on_complete(ok, msg):
                    if ok:
                        self.show_info(_('Payment sent.'))
                        if self.send_screen:
                            self.send_screen.do_clear()
       -                if pr:
       -                    self.wallet.invoices.set_paid(pr, tx.txid())
       -                    self.wallet.invoices.save()
       +                if invoice:
       +                    key = invoice['id']
       +                    txid = tx.txid()
       +                    self.wallet.set_label(txid, invoice['message'])
       +                    self.wallet.set_paid(key, txid)
                            self.update_tab('invoices')
                    else:
                        msg = msg or ''
   DIR diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
       t@@ -171,7 +171,6 @@ class HistoryScreen(CScreen):
                return ri
        
            def update(self, see_all=False):
       -        import operator
                wallet = self.app.wallet
                if wallet is None:
                    return
       t@@ -232,8 +231,7 @@ class SendScreen(CScreen):
        
            def get_card(self, item):
                invoice_type = item['type']
       -        status = item['status']
       -        status_str = get_request_status(item) # convert to str
       +        status, status_str = get_request_status(item) # convert to str
                if invoice_type == PR_TYPE_LN:
                    key = item['rhash']
                    log = self.app.wallet.lnworker.logs.get(key)
       t@@ -336,13 +334,10 @@ class SendScreen(CScreen):
        
            def do_pay_invoice(self, invoice):
                if invoice['type'] == PR_TYPE_LN:
       -            self._do_send_lightning(invoice['invoice'], invoice['amount'])
       +            self._do_pay_lightning(invoice)
                    return
                elif invoice['type'] == PR_TYPE_ONCHAIN:
       -            message = invoice['message']
       -            outputs = invoice['outputs']  # type: List[TxOutput]
       -            amount = sum(map(lambda x: x.value, outputs))
       -            do_pay = lambda rbf: self._do_send_onchain(amount, message, outputs, rbf)
       +            do_pay = lambda rbf: self._do_pay_onchain(invoice, rbf)
                    if self.app.electrum_config.get('use_rbf'):
                        d = Question(_('Should this transaction be replaceable?'), do_pay)
                        d.open()
       t@@ -351,12 +346,14 @@ class SendScreen(CScreen):
                else:
                    raise Exception('unknown invoice type')
        
       -    def _do_send_lightning(self, invoice, amount):
       +    def _do_pay_lightning(self, invoice):
                attempts = 10
       -        threading.Thread(target=self.app.wallet.lnworker.pay, args=(invoice, amount, attempts)).start()
       +        threading.Thread(target=self.app.wallet.lnworker.pay, args=(invoice['invoice'], invoice['amount'], attempts)).start()
        
       -    def _do_send_onchain(self, amount, message, outputs, rbf):
       +    def _do_pay_onchain(self, invoice, rbf):
                # make unsigned transaction
       +        outputs = invoice['outputs']  # type: List[TxOutput]
       +        amount = sum(map(lambda x: x.value, outputs))
                coins = self.app.wallet.get_spendable_coins(None)
                try:
                    tx = self.app.wallet.make_unsigned_transaction(coins, outputs, None)
       t@@ -383,15 +380,14 @@ class SendScreen(CScreen):
                if fee > feerate_warning * tx.estimated_size() / 1000:
                    msg.append(_('Warning') + ': ' + _("The fee for this transaction seems unusually high."))
                msg.append(_("Enter your PIN code to proceed"))
       -        self.app.protected('\n'.join(msg), self.send_tx, (tx, message))
       +        self.app.protected('\n'.join(msg), self.send_tx, (tx, invoice))
        
       -    def send_tx(self, tx, message, password):
       +    def send_tx(self, tx, invoice, password):
                if self.app.wallet.has_password() and password is None:
                    return
                def on_success(tx):
                    if tx.is_complete():
       -                self.app.broadcast(tx, self.payment_request)
       -                self.app.wallet.set_label(tx.txid(), message)
       +                self.app.broadcast(tx, invoice)
                    else:
                        self.app.tx_dialog(tx)
                def on_failure(error):
       t@@ -477,6 +473,7 @@ class ReceiveScreen(CScreen):
                    address = req['invoice']
                amount = req.get('amount')
                description = req.get('memo', '')
       +        status, status_str = get_request_status(req)
                ci = {}
                ci['screen'] = self
                ci['address'] = address
       t@@ -484,8 +481,8 @@ class ReceiveScreen(CScreen):
                ci['key'] = key
                ci['amount'] = self.app.format_amount_and_units(amount) if amount else ''
                ci['memo'] = description
       -        ci['status'] = get_request_status(req)
       -        ci['is_expired'] = req['status'] == PR_EXPIRED
       +        ci['status'] = status_str
       +        ci['is_expired'] = status == PR_EXPIRED
                return ci
        
            def update(self):
   DIR diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py
       t@@ -84,7 +84,7 @@ class InvoiceList(MyTreeView):
                else:
                    return
                status_item = model.item(row, self.Columns.STATUS)
       -        status_str = get_request_status(req)
       +        status, status_str = get_request_status(req)
                log = self.parent.wallet.lnworker.logs.get(key)
                if log and status == PR_INFLIGHT:
                    status_str += '... (%d)'%len(log)
       t@@ -109,8 +109,7 @@ class InvoiceList(MyTreeView):
                            icon_name = 'seal.png'
                    else:
                        raise Exception('Unsupported type')
       -            status = item['status']
       -            status_str = get_request_status(item) # convert to str
       +            status, status_str = get_request_status(item)
                    message = item['message']
                    amount = item['amount']
                    timestamp = item.get('time', 0)
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -1856,33 +1856,32 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                msg = _('Signing transaction...')
                WaitingDialog(self, msg, task, on_success, on_failure)
        
       -    def broadcast_transaction(self, tx, tx_desc):
       +    def broadcast_transaction(self, tx, invoice=None):
        
                def broadcast_thread():
                    # non-GUI thread
                    pr = self.payment_request
                    if pr and pr.has_expired():
                        self.payment_request = None
       -                return False, _("Payment request has expired")
       -            status = False
       +                return False, _("Invoice has expired")
                    try:
                        self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
                    except TxBroadcastError as e:
       -                msg = e.get_message_for_gui()
       +                return False, e.get_message_for_gui()
                    except BestEffortRequestFailed as e:
       -                msg = repr(e)
       -            else:
       -                status, msg = True, tx.txid()
       -            if pr and status is True:
       -                key = pr.get_id()
       -                #self.wallet.set_invoice_paid(key, tx.txid())
       +                return False, repr(e)
       +            # success
       +            key = invoice['id']
       +            txid = tx.txid()
       +            self.wallet.set_paid(key, txid)
       +            if pr:
                        self.payment_request = None
                        refund_address = self.wallet.get_receiving_address()
                        coro = pr.send_payment_and_receive_paymentack(str(tx), refund_address)
                        fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
                        ack_status, ack_msg = fut.result(timeout=20)
                        self.logger.info(f"Payment ACK: {ack_status}. Ack message: {ack_msg}")
       -            return status, msg
       +            return True, txid
        
                # Capture current TL window; override might be removed on return
                parent = self.top_level_window(lambda win: isinstance(win, MessageBoxMixin))
   DIR diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py
       t@@ -103,8 +103,7 @@ class RequestList(MyTreeView):
                    is_lightning = date_item.data(ROLE_REQUEST_TYPE) == PR_TYPE_LN
                    req = self.wallet.get_request(key)
                    if req:
       -                status = req['status']
       -                status_str = get_request_status(req)
       +                status, status_str = get_request_status(req)
                        status_item.setText(status_str)
                        status_item.setIcon(read_QIcon(pr_icons.get(status)))
        
       t@@ -115,7 +114,7 @@ class RequestList(MyTreeView):
                self.model().clear()
                self.update_headers(self.__class__.headers)
                for req in self.wallet.get_sorted_requests():
       -            status = req.get('status')
       +            status, status_str = get_request_status(req)
                    if status == PR_PAID:
                        continue
                    request_type = req['type']
       t@@ -125,7 +124,6 @@ class RequestList(MyTreeView):
                    message = req.get('message') or req.get('memo')
                    date = format_time(timestamp)
                    amount_str = self.parent.format_amount(amount) if amount else ""
       -            status_str = get_request_status(req)
                    labels = [date, message, amount_str, status_str]
                    if request_type == PR_TYPE_LN:
                        key = req['rhash']
   DIR diff --git a/electrum/util.py b/electrum/util.py
       t@@ -112,6 +112,8 @@ pr_expiration_values = {
        
        def get_request_status(req):
            status = req['status']
       +    if req['status'] == PR_UNPAID and 'exp' in req and req['time'] + req['exp'] < time.time():
       +        status = PR_EXPIRED
            status_str = pr_tooltips[status]
            if status == PR_UNPAID:
                if req.get('exp'):
       t@@ -119,7 +121,7 @@ def get_request_status(req):
                    status_str = _('Expires') + ' ' + age(expiration, include_seconds=True)
                else:
                    status_str = _('Pending')
       -    return status_str
       +    return status, status_str
        
        
        class UnknownBaseUnit(Exception): pass
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -587,9 +587,13 @@ class Abstract_Wallet(AddressSynchronizer):
                out.sort(key=operator.itemgetter('time'))
                return out
        
       -    def check_if_expired(self, item):
       -        if item['status'] == PR_UNPAID and 'exp' in item and item['time'] + item['exp'] < time.time():
       -            item['status'] = PR_EXPIRED
       +    def set_paid(self, key, txid):
       +        if key not in self.invoices:
       +            return
       +        invoice = self.invoices[key]
       +        assert invoice.get('type') == PR_TYPE_ONCHAIN
       +        invoice['txid'] = txid
       +        self.storage.put('invoices', self.invoices)
        
            def get_invoice(self, key):
                if key not in self.invoices:
       t@@ -602,7 +606,6 @@ class Abstract_Wallet(AddressSynchronizer):
                    item['status'] = self.lnworker.get_payment_status(bfh(item['rhash']))
                else:
                    return
       -        self.check_if_expired(item)
                return item
        
            @profiler
       t@@ -1397,7 +1400,6 @@ class Abstract_Wallet(AddressSynchronizer):
                    req['status'] = self.lnworker.get_payment_status(bfh(key))
                else:
                    return
       -        self.check_if_expired(req)
                # add URL if we are running a payserver
                if self.config.get('run_payserver'):
                    host = self.config.get('payserver_host', 'localhost')