Browse Source

invoices: Use the same base method to export invoices and requests.

This fixes an inconsistency where the 'expiration' field was
relative for invoices, and absolute timestamp for requests.

This in turn fixes QML the timer refreshing the request list.

In order to prevent any API using that field from being silently
broken, the 'expiration' field is renamed as 'expiry'.
master
ThomasV 3 years ago
parent
commit
56e685feaa
  1. 8
      electrum/commands.py
  2. 6
      electrum/gui/qml/qeinvoicelistmodel.py
  3. 19
      electrum/invoices.py
  4. 42
      electrum/wallet.py

8
electrum/commands.py

@ -975,7 +975,7 @@ class Commands:
return wallet.get_unused_address()
@command('w')
async def add_request(self, amount, memo='', expiration=3600, force=False, wallet: Abstract_Wallet = None):
async def add_request(self, amount, memo='', expiry=3600, force=False, wallet: Abstract_Wallet = None):
"""Create a payment request, using the first unused address of the wallet.
The address will be considered as used after this operation.
If no payment is received, the address will be considered as unused if the payment request is deleted from the wallet."""
@ -986,8 +986,8 @@ class Commands:
else:
return False
amount = satoshis(amount)
expiration = int(expiration) if expiration else None
key = wallet.create_request(amount, memo, expiration, addr)
expiry = int(expiry) if expiry else None
key = wallet.create_request(amount, memo, expiry, addr)
req = wallet.get_request(key)
return wallet.export_request(req)
@ -1429,7 +1429,7 @@ command_options = {
'addtransaction': (None,'Whether transaction is to be used for broadcasting afterwards. Adds transaction to the wallet'),
'domain': ("-D", "List of addresses"),
'memo': ("-m", "Description of the request"),
'expiration': (None, "Time in seconds"),
'expiry': (None, "Time in seconds"),
'timeout': (None, "Timeout in seconds"),
'force': (None, "Create new address beyond gap limit, if no more addresses are available."),
'pending': (None, "Show only pending requests."),

6
electrum/gui/qml/qeinvoicelistmodel.py

@ -15,7 +15,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
# define listmodel rolemap
_ROLE_NAMES=('key', 'is_lightning', 'timestamp', 'date', 'message', 'amount',
'status', 'status_str', 'address', 'expiration', 'type', 'onchain_fallback',
'status', 'status_str', 'address', 'expiry', 'type', 'onchain_fallback',
'lightning_invoice')
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES]))
@ -132,8 +132,8 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
nearest_interval = LN_EXPIRY_NEVER
for invoice in self.invoices:
if invoice['status'] != PR_EXPIRED:
if invoice['expiration'] > 0 and invoice['expiration'] != LN_EXPIRY_NEVER:
interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiration'])
if invoice['expiry'] > 0 and invoice['expiry'] != LN_EXPIRY_NEVER:
interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiry'])
if interval > 0:
nearest_interval = nearest_interval if nearest_interval < interval else interval

19
electrum/invoices.py

@ -6,7 +6,7 @@ import attr
from .json_db import StoredObject
from .i18n import _
from .util import age, InvoiceError
from .util import age, InvoiceError, format_satoshis
from .lnutil import hex_to_bytes
from .lnaddr import lndecode, LnAddr
from . import constants
@ -222,6 +222,23 @@ class BaseInvoice(StoredObject):
else: # on-chain
return get_id_from_onchain_outputs(outputs=self.get_outputs(), timestamp=self.time)
def as_dict(self, status):
d = {
'is_lightning': self.is_lightning(),
'amount_BTC': format_satoshis(self.get_amount_sat()),
'message': self.message,
'timestamp': self.get_time(),
'expiry': self.exp,
'status': status,
'status_str': self.get_status_str(status),
'id': self.get_id(),
'amount_sat': int(self.get_amount_sat()),
}
if self.is_lightning():
d['amount_msat'] = self.get_amount_msat()
return d
@attr.s
class Invoice(BaseInvoice):
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]

42
electrum/wallet.py

@ -2459,34 +2459,20 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def export_request(self, x: Request) -> Dict[str, Any]:
key = x.get_id()
status = self.get_invoice_status(x)
status_str = x.get_status_str(status)
is_lightning = x.is_lightning()
address = x.get_address()
d = {
'is_lightning': is_lightning,
'amount_BTC': format_satoshis(x.get_amount_sat()),
'message': x.message,
'timestamp': x.get_time(),
'expiration': x.get_expiration_date(),
'status': status,
'status_str': status_str,
'request_id': key,
"tx_hashes": []
}
if is_lightning:
d = x.as_dict(status)
d['request_id'] = d.pop('id')
if x.is_lightning():
d['rhash'] = x.rhash
d['lightning_invoice'] = self.get_bolt11_invoice(x)
d['amount_msat'] = x.get_amount_msat()
if self.lnworker and status == PR_UNPAID:
d['can_receive'] = self.lnworker.can_receive_invoice(x)
if address:
d['amount_sat'] = int(x.get_amount_sat())
if address := x.get_address():
d['address'] = address
d['URI'] = self.get_request_URI(x)
# if request was paid onchain, add relevant fields
# note: addr is reused when getting paid on LN! so we check for that.
_, conf, tx_hashes = self._is_onchain_invoice_paid(x)
if not is_lightning or not self.lnworker or self.lnworker.get_invoice_status(x) != PR_PAID:
if not x.is_lightning() or not self.lnworker or self.lnworker.get_invoice_status(x) != PR_PAID:
if conf is not None:
d['confirmations'] = conf
d['tx_hashes'] = tx_hashes
@ -2496,27 +2482,15 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def export_invoice(self, x: Invoice) -> Dict[str, Any]:
key = x.get_id()
status = self.get_invoice_status(x)
status_str = x.get_status_str(status)
is_lightning = x.is_lightning()
d = {
'is_lightning': is_lightning,
'amount_BTC': format_satoshis(x.get_amount_sat()),
'message': x.message,
'timestamp': x.time,
'expiration': x.exp,
'status': status,
'status_str': status_str,
'invoice_id': key,
}
if is_lightning:
d = x.as_dict(status)
d['invoice_id'] = d.pop('id')
if x.is_lightning():
d['lightning_invoice'] = x.lightning_invoice
d['amount_msat'] = x.get_amount_msat()
if self.lnworker and status == PR_UNPAID:
d['can_pay'] = self.lnworker.can_pay_invoice(x)
else:
amount_sat = x.get_amount_sat()
assert isinstance(amount_sat, (int, str, type(None)))
d['amount_sat'] = amount_sat
d['outputs'] = [y.to_legacy_tuple() for y in x.get_outputs()]
if x.bip70:
d['bip70'] = x.bip70

Loading…
Cancel
Save