Call wallet.set_paid after onchain broadcast. Check if invoices are expired in util.get_request_status
This commit is contained in:
@@ -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 ''
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user