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'.
This commit is contained in:
@@ -975,7 +975,7 @@ class Commands:
|
|||||||
return wallet.get_unused_address()
|
return wallet.get_unused_address()
|
||||||
|
|
||||||
@command('w')
|
@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.
|
"""Create a payment request, using the first unused address of the wallet.
|
||||||
The address will be considered as used after this operation.
|
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."""
|
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:
|
else:
|
||||||
return False
|
return False
|
||||||
amount = satoshis(amount)
|
amount = satoshis(amount)
|
||||||
expiration = int(expiration) if expiration else None
|
expiry = int(expiry) if expiry else None
|
||||||
key = wallet.create_request(amount, memo, expiration, addr)
|
key = wallet.create_request(amount, memo, expiry, addr)
|
||||||
req = wallet.get_request(key)
|
req = wallet.get_request(key)
|
||||||
return wallet.export_request(req)
|
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'),
|
'addtransaction': (None,'Whether transaction is to be used for broadcasting afterwards. Adds transaction to the wallet'),
|
||||||
'domain': ("-D", "List of addresses"),
|
'domain': ("-D", "List of addresses"),
|
||||||
'memo': ("-m", "Description of the request"),
|
'memo': ("-m", "Description of the request"),
|
||||||
'expiration': (None, "Time in seconds"),
|
'expiry': (None, "Time in seconds"),
|
||||||
'timeout': (None, "Timeout in seconds"),
|
'timeout': (None, "Timeout in seconds"),
|
||||||
'force': (None, "Create new address beyond gap limit, if no more addresses are available."),
|
'force': (None, "Create new address beyond gap limit, if no more addresses are available."),
|
||||||
'pending': (None, "Show only pending requests."),
|
'pending': (None, "Show only pending requests."),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
|
|||||||
|
|
||||||
# define listmodel rolemap
|
# define listmodel rolemap
|
||||||
_ROLE_NAMES=('key', 'is_lightning', 'timestamp', 'date', 'message', 'amount',
|
_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')
|
'lightning_invoice')
|
||||||
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
|
_ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES))
|
||||||
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _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
|
nearest_interval = LN_EXPIRY_NEVER
|
||||||
for invoice in self.invoices:
|
for invoice in self.invoices:
|
||||||
if invoice['status'] != PR_EXPIRED:
|
if invoice['status'] != PR_EXPIRED:
|
||||||
if invoice['expiration'] > 0 and invoice['expiration'] != LN_EXPIRY_NEVER:
|
if invoice['expiry'] > 0 and invoice['expiry'] != LN_EXPIRY_NEVER:
|
||||||
interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiration'])
|
interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiry'])
|
||||||
if interval > 0:
|
if interval > 0:
|
||||||
nearest_interval = nearest_interval if nearest_interval < interval else interval
|
nearest_interval = nearest_interval if nearest_interval < interval else interval
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import attr
|
|||||||
|
|
||||||
from .json_db import StoredObject
|
from .json_db import StoredObject
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .util import age, InvoiceError
|
from .util import age, InvoiceError, format_satoshis
|
||||||
from .lnutil import hex_to_bytes
|
from .lnutil import hex_to_bytes
|
||||||
from .lnaddr import lndecode, LnAddr
|
from .lnaddr import lndecode, LnAddr
|
||||||
from . import constants
|
from . import constants
|
||||||
@@ -222,6 +222,23 @@ class BaseInvoice(StoredObject):
|
|||||||
else: # on-chain
|
else: # on-chain
|
||||||
return get_id_from_onchain_outputs(outputs=self.get_outputs(), timestamp=self.time)
|
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
|
@attr.s
|
||||||
class Invoice(BaseInvoice):
|
class Invoice(BaseInvoice):
|
||||||
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
|
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
|
||||||
|
|||||||
@@ -2459,34 +2459,20 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
def export_request(self, x: Request) -> Dict[str, Any]:
|
def export_request(self, x: Request) -> Dict[str, Any]:
|
||||||
key = x.get_id()
|
key = x.get_id()
|
||||||
status = self.get_invoice_status(x)
|
status = self.get_invoice_status(x)
|
||||||
status_str = x.get_status_str(status)
|
d = x.as_dict(status)
|
||||||
is_lightning = x.is_lightning()
|
d['request_id'] = d.pop('id')
|
||||||
address = x.get_address()
|
if x.is_lightning():
|
||||||
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['rhash'] = x.rhash
|
d['rhash'] = x.rhash
|
||||||
d['lightning_invoice'] = self.get_bolt11_invoice(x)
|
d['lightning_invoice'] = self.get_bolt11_invoice(x)
|
||||||
d['amount_msat'] = x.get_amount_msat()
|
|
||||||
if self.lnworker and status == PR_UNPAID:
|
if self.lnworker and status == PR_UNPAID:
|
||||||
d['can_receive'] = self.lnworker.can_receive_invoice(x)
|
d['can_receive'] = self.lnworker.can_receive_invoice(x)
|
||||||
if address:
|
if address := x.get_address():
|
||||||
d['amount_sat'] = int(x.get_amount_sat())
|
|
||||||
d['address'] = address
|
d['address'] = address
|
||||||
d['URI'] = self.get_request_URI(x)
|
d['URI'] = self.get_request_URI(x)
|
||||||
# if request was paid onchain, add relevant fields
|
# if request was paid onchain, add relevant fields
|
||||||
# note: addr is reused when getting paid on LN! so we check for that.
|
# note: addr is reused when getting paid on LN! so we check for that.
|
||||||
_, conf, tx_hashes = self._is_onchain_invoice_paid(x)
|
_, 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:
|
if conf is not None:
|
||||||
d['confirmations'] = conf
|
d['confirmations'] = conf
|
||||||
d['tx_hashes'] = tx_hashes
|
d['tx_hashes'] = tx_hashes
|
||||||
@@ -2496,27 +2482,15 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
def export_invoice(self, x: Invoice) -> Dict[str, Any]:
|
def export_invoice(self, x: Invoice) -> Dict[str, Any]:
|
||||||
key = x.get_id()
|
key = x.get_id()
|
||||||
status = self.get_invoice_status(x)
|
status = self.get_invoice_status(x)
|
||||||
status_str = x.get_status_str(status)
|
d = x.as_dict(status)
|
||||||
is_lightning = x.is_lightning()
|
d['invoice_id'] = d.pop('id')
|
||||||
d = {
|
if x.is_lightning():
|
||||||
'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['lightning_invoice'] = x.lightning_invoice
|
d['lightning_invoice'] = x.lightning_invoice
|
||||||
d['amount_msat'] = x.get_amount_msat()
|
|
||||||
if self.lnworker and status == PR_UNPAID:
|
if self.lnworker and status == PR_UNPAID:
|
||||||
d['can_pay'] = self.lnworker.can_pay_invoice(x)
|
d['can_pay'] = self.lnworker.can_pay_invoice(x)
|
||||||
else:
|
else:
|
||||||
amount_sat = x.get_amount_sat()
|
amount_sat = x.get_amount_sat()
|
||||||
assert isinstance(amount_sat, (int, str, type(None)))
|
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()]
|
d['outputs'] = [y.to_legacy_tuple() for y in x.get_outputs()]
|
||||||
if x.bip70:
|
if x.bip70:
|
||||||
d['bip70'] = x.bip70
|
d['bip70'] = x.bip70
|
||||||
|
|||||||
Reference in New Issue
Block a user