Browse Source

Call wallet.set_paid after onchain broadcast. Check if invoices are expired in util.get_request_status

master
ThomasV 6 years ago
parent
commit
a0ec2690cf
  1. 10
      electrum/gui/kivy/main_window.py
  2. 31
      electrum/gui/kivy/uix/screens.py
  3. 5
      electrum/gui/qt/invoice_list.py
  4. 21
      electrum/gui/qt/main_window.py
  5. 6
      electrum/gui/qt/request_list.py
  6. 4
      electrum/util.py
  7. 12
      electrum/wallet.py

10
electrum/gui/kivy/main_window.py

@ -1014,15 +1014,17 @@ class ElectrumWindow(App):
status, msg = True, tx.txid() status, msg = True, tx.txid()
Clock.schedule_once(lambda dt: on_complete(status, msg)) 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): def on_complete(ok, msg):
if ok: if ok:
self.show_info(_('Payment sent.')) self.show_info(_('Payment sent.'))
if self.send_screen: if self.send_screen:
self.send_screen.do_clear() self.send_screen.do_clear()
if pr: if invoice:
self.wallet.invoices.set_paid(pr, tx.txid()) key = invoice['id']
self.wallet.invoices.save() txid = tx.txid()
self.wallet.set_label(txid, invoice['message'])
self.wallet.set_paid(key, txid)
self.update_tab('invoices') self.update_tab('invoices')
else: else:
msg = msg or '' msg = msg or ''

31
electrum/gui/kivy/uix/screens.py

@ -171,7 +171,6 @@ class HistoryScreen(CScreen):
return ri return ri
def update(self, see_all=False): def update(self, see_all=False):
import operator
wallet = self.app.wallet wallet = self.app.wallet
if wallet is None: if wallet is None:
return return
@ -232,8 +231,7 @@ class SendScreen(CScreen):
def get_card(self, item): def get_card(self, item):
invoice_type = item['type'] invoice_type = item['type']
status = item['status'] status, status_str = get_request_status(item) # convert to str
status_str = get_request_status(item) # convert to str
if invoice_type == PR_TYPE_LN: if invoice_type == PR_TYPE_LN:
key = item['rhash'] key = item['rhash']
log = self.app.wallet.lnworker.logs.get(key) log = self.app.wallet.lnworker.logs.get(key)
@ -336,13 +334,10 @@ class SendScreen(CScreen):
def do_pay_invoice(self, invoice): def do_pay_invoice(self, invoice):
if invoice['type'] == PR_TYPE_LN: if invoice['type'] == PR_TYPE_LN:
self._do_send_lightning(invoice['invoice'], invoice['amount']) self._do_pay_lightning(invoice)
return return
elif invoice['type'] == PR_TYPE_ONCHAIN: elif invoice['type'] == PR_TYPE_ONCHAIN:
message = invoice['message'] do_pay = lambda rbf: self._do_pay_onchain(invoice, rbf)
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)
if self.app.electrum_config.get('use_rbf'): if self.app.electrum_config.get('use_rbf'):
d = Question(_('Should this transaction be replaceable?'), do_pay) d = Question(_('Should this transaction be replaceable?'), do_pay)
d.open() d.open()
@ -351,12 +346,14 @@ class SendScreen(CScreen):
else: else:
raise Exception('unknown invoice type') raise Exception('unknown invoice type')
def _do_send_lightning(self, invoice, amount): def _do_pay_lightning(self, invoice):
attempts = 10 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 # 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) coins = self.app.wallet.get_spendable_coins(None)
try: try:
tx = self.app.wallet.make_unsigned_transaction(coins, outputs, None) 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: if fee > feerate_warning * tx.estimated_size() / 1000:
msg.append(_('Warning') + ': ' + _("The fee for this transaction seems unusually high.")) msg.append(_('Warning') + ': ' + _("The fee for this transaction seems unusually high."))
msg.append(_("Enter your PIN code to proceed")) 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: if self.app.wallet.has_password() and password is None:
return return
def on_success(tx): def on_success(tx):
if tx.is_complete(): if tx.is_complete():
self.app.broadcast(tx, self.payment_request) self.app.broadcast(tx, invoice)
self.app.wallet.set_label(tx.txid(), message)
else: else:
self.app.tx_dialog(tx) self.app.tx_dialog(tx)
def on_failure(error): def on_failure(error):
@ -477,6 +473,7 @@ class ReceiveScreen(CScreen):
address = req['invoice'] address = req['invoice']
amount = req.get('amount') amount = req.get('amount')
description = req.get('memo', '') description = req.get('memo', '')
status, status_str = get_request_status(req)
ci = {} ci = {}
ci['screen'] = self ci['screen'] = self
ci['address'] = address ci['address'] = address
@ -484,8 +481,8 @@ class ReceiveScreen(CScreen):
ci['key'] = key ci['key'] = key
ci['amount'] = self.app.format_amount_and_units(amount) if amount else '' ci['amount'] = self.app.format_amount_and_units(amount) if amount else ''
ci['memo'] = description ci['memo'] = description
ci['status'] = get_request_status(req) ci['status'] = status_str
ci['is_expired'] = req['status'] == PR_EXPIRED ci['is_expired'] = status == PR_EXPIRED
return ci return ci
def update(self): def update(self):

5
electrum/gui/qt/invoice_list.py

@ -84,7 +84,7 @@ class InvoiceList(MyTreeView):
else: else:
return return
status_item = model.item(row, self.Columns.STATUS) 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) log = self.parent.wallet.lnworker.logs.get(key)
if log and status == PR_INFLIGHT: if log and status == PR_INFLIGHT:
status_str += '... (%d)'%len(log) status_str += '... (%d)'%len(log)
@ -109,8 +109,7 @@ class InvoiceList(MyTreeView):
icon_name = 'seal.png' icon_name = 'seal.png'
else: else:
raise Exception('Unsupported type') raise Exception('Unsupported type')
status = item['status'] status, status_str = get_request_status(item)
status_str = get_request_status(item) # convert to str
message = item['message'] message = item['message']
amount = item['amount'] amount = item['amount']
timestamp = item.get('time', 0) timestamp = item.get('time', 0)

21
electrum/gui/qt/main_window.py

@ -1856,33 +1856,32 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
msg = _('Signing transaction...') msg = _('Signing transaction...')
WaitingDialog(self, msg, task, on_success, on_failure) 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(): def broadcast_thread():
# non-GUI thread # non-GUI thread
pr = self.payment_request pr = self.payment_request
if pr and pr.has_expired(): if pr and pr.has_expired():
self.payment_request = None self.payment_request = None
return False, _("Payment request has expired") return False, _("Invoice has expired")
status = False
try: try:
self.network.run_from_another_thread(self.network.broadcast_transaction(tx)) self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
except TxBroadcastError as e: except TxBroadcastError as e:
msg = e.get_message_for_gui() return False, e.get_message_for_gui()
except BestEffortRequestFailed as e: except BestEffortRequestFailed as e:
msg = repr(e) return False, repr(e)
else: # success
status, msg = True, tx.txid() key = invoice['id']
if pr and status is True: txid = tx.txid()
key = pr.get_id() self.wallet.set_paid(key, txid)
#self.wallet.set_invoice_paid(key, tx.txid()) if pr:
self.payment_request = None self.payment_request = None
refund_address = self.wallet.get_receiving_address() refund_address = self.wallet.get_receiving_address()
coro = pr.send_payment_and_receive_paymentack(str(tx), refund_address) coro = pr.send_payment_and_receive_paymentack(str(tx), refund_address)
fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop) fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
ack_status, ack_msg = fut.result(timeout=20) ack_status, ack_msg = fut.result(timeout=20)
self.logger.info(f"Payment ACK: {ack_status}. Ack message: {ack_msg}") 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 # Capture current TL window; override might be removed on return
parent = self.top_level_window(lambda win: isinstance(win, MessageBoxMixin)) parent = self.top_level_window(lambda win: isinstance(win, MessageBoxMixin))

6
electrum/gui/qt/request_list.py

@ -103,8 +103,7 @@ class RequestList(MyTreeView):
is_lightning = date_item.data(ROLE_REQUEST_TYPE) == PR_TYPE_LN is_lightning = date_item.data(ROLE_REQUEST_TYPE) == PR_TYPE_LN
req = self.wallet.get_request(key) req = self.wallet.get_request(key)
if req: if req:
status = req['status'] status, status_str = get_request_status(req)
status_str = get_request_status(req)
status_item.setText(status_str) status_item.setText(status_str)
status_item.setIcon(read_QIcon(pr_icons.get(status))) status_item.setIcon(read_QIcon(pr_icons.get(status)))
@ -115,7 +114,7 @@ class RequestList(MyTreeView):
self.model().clear() self.model().clear()
self.update_headers(self.__class__.headers) self.update_headers(self.__class__.headers)
for req in self.wallet.get_sorted_requests(): for req in self.wallet.get_sorted_requests():
status = req.get('status') status, status_str = get_request_status(req)
if status == PR_PAID: if status == PR_PAID:
continue continue
request_type = req['type'] request_type = req['type']
@ -125,7 +124,6 @@ class RequestList(MyTreeView):
message = req.get('message') or req.get('memo') message = req.get('message') or req.get('memo')
date = format_time(timestamp) date = format_time(timestamp)
amount_str = self.parent.format_amount(amount) if amount else "" amount_str = self.parent.format_amount(amount) if amount else ""
status_str = get_request_status(req)
labels = [date, message, amount_str, status_str] labels = [date, message, amount_str, status_str]
if request_type == PR_TYPE_LN: if request_type == PR_TYPE_LN:
key = req['rhash'] key = req['rhash']

4
electrum/util.py

@ -112,6 +112,8 @@ pr_expiration_values = {
def get_request_status(req): def get_request_status(req):
status = req['status'] 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] status_str = pr_tooltips[status]
if status == PR_UNPAID: if status == PR_UNPAID:
if req.get('exp'): if req.get('exp'):
@ -119,7 +121,7 @@ def get_request_status(req):
status_str = _('Expires') + ' ' + age(expiration, include_seconds=True) status_str = _('Expires') + ' ' + age(expiration, include_seconds=True)
else: else:
status_str = _('Pending') status_str = _('Pending')
return status_str return status, status_str
class UnknownBaseUnit(Exception): pass class UnknownBaseUnit(Exception): pass

12
electrum/wallet.py

@ -587,9 +587,13 @@ class Abstract_Wallet(AddressSynchronizer):
out.sort(key=operator.itemgetter('time')) out.sort(key=operator.itemgetter('time'))
return out return out
def check_if_expired(self, item): def set_paid(self, key, txid):
if item['status'] == PR_UNPAID and 'exp' in item and item['time'] + item['exp'] < time.time(): if key not in self.invoices:
item['status'] = PR_EXPIRED 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): def get_invoice(self, key):
if key not in self.invoices: if key not in self.invoices:
@ -602,7 +606,6 @@ class Abstract_Wallet(AddressSynchronizer):
item['status'] = self.lnworker.get_payment_status(bfh(item['rhash'])) item['status'] = self.lnworker.get_payment_status(bfh(item['rhash']))
else: else:
return return
self.check_if_expired(item)
return item return item
@profiler @profiler
@ -1397,7 +1400,6 @@ class Abstract_Wallet(AddressSynchronizer):
req['status'] = self.lnworker.get_payment_status(bfh(key)) req['status'] = self.lnworker.get_payment_status(bfh(key))
else: else:
return return
self.check_if_expired(req)
# add URL if we are running a payserver # add URL if we are running a payserver
if self.config.get('run_payserver'): if self.config.get('run_payserver'):
host = self.config.get('payserver_host', 'localhost') host = self.config.get('payserver_host', 'localhost')

Loading…
Cancel
Save