electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit a0ec2690cf0a1c36bc468fbfb5aec1954eff72ae
parent e35bddcc09ccc9a5f761ccf3c4bce5cdab64f4c6
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:
Melectrum/gui/kivy/main_window.py | 10++++++----
Melectrum/gui/kivy/uix/screens.py | 31++++++++++++++-----------------
Melectrum/gui/qt/invoice_list.py | 5++---
Melectrum/gui/qt/main_window.py | 21++++++++++-----------
Melectrum/gui/qt/request_list.py | 6++----
Melectrum/util.py | 4+++-
Melectrum/wallet.py | 12+++++++-----
7 files changed, 44 insertions(+), 45 deletions(-)

diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py @@ -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 '' diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py @@ -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 @@ -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) @@ -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() @@ -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) @@ -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): @@ -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 @@ -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): diff --git a/electrum/gui/qt/invoice_list.py b/electrum/gui/qt/invoice_list.py @@ -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) @@ -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) diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py @@ -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)) diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py @@ -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))) @@ -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'] @@ -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'] diff --git a/electrum/util.py b/electrum/util.py @@ -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'): @@ -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 diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -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: @@ -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 @@ -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')