1
0

Refresh bolt11 routing hints when channel liquidity changes:

- wallet_db update: separate Invoices and Requests.
 - do not store bolt11 invoice in Request
This commit is contained in:
ThomasV
2023-02-27 10:31:21 +01:00
parent c7cb2fb9e6
commit 719b468eee
11 changed files with 154 additions and 112 deletions

View File

@@ -7,6 +7,7 @@ import attr
from .json_db import StoredObject
from .i18n import _
from .util import age, InvoiceError
from .lnutil import hex_to_bytes
from .lnaddr import lndecode, LnAddr
from . import constants
from .bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
@@ -83,7 +84,11 @@ LN_EXPIRY_NEVER = 100 * 365 * 24 * 60 * 60 # 100 years
@attr.s
class Invoice(StoredObject):
class BaseInvoice(StoredObject):
"""
Base class for Invoice and Request
In the code, we use 'invoice' for outgoing payments, and 'request' for incoming payments.
"""
# mandatory fields
amount_msat = attr.ib(kw_only=True) # type: Optional[Union[int, str]] # can be '!' or None
@@ -101,10 +106,6 @@ class Invoice(StoredObject):
bip70 = attr.ib(type=str, kw_only=True) # type: Optional[str]
#bip70_requestor = attr.ib(type=str, kw_only=True) # type: Optional[str]
# lightning only
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
__lnaddr = None
def is_lightning(self):
return self.lightning_invoice is not None
@@ -117,15 +118,6 @@ class Invoice(StoredObject):
status_str = _('Expires') + ' ' + age(expiration, include_seconds=True)
return status_str
def get_address(self) -> Optional[str]:
"""returns the first address, to be displayed in GUI"""
address = None
if self.outputs:
address = self.outputs[0].address if len(self.outputs) > 0 else None
if not address and self.is_lightning():
address = self._lnaddr.get_fallback_address() or None
return address
def get_outputs(self) -> Sequence[PartialTxOutput]:
outputs = self.outputs or []
if not outputs:
@@ -135,12 +127,6 @@ class Invoice(StoredObject):
outputs = [PartialTxOutput.from_address_and_value(address, int(amount))]
return outputs
def can_be_paid_onchain(self) -> bool:
if self.is_lightning():
return bool(self._lnaddr.get_fallback_address())
else:
return True
def get_expiration_date(self):
# 0 means never
return self.exp + self.time if self.exp else 0
@@ -193,12 +179,6 @@ class Invoice(StoredObject):
uri = create_bip21_uri(addr, amount, message, extra_query_params=extra)
return str(uri)
@lightning_invoice.validator
def _validate_invoice_str(self, attribute, value):
if value is not None:
lnaddr = lndecode(value) # this checks the str can be decoded
self.__lnaddr = lnaddr # save it, just to avoid having to recompute later
@amount_msat.validator
def _validate_amount(self, attribute, value):
if value is None:
@@ -212,16 +192,6 @@ class Invoice(StoredObject):
else:
raise InvoiceError(f"unexpected amount: {value!r}")
@property
def _lnaddr(self) -> LnAddr:
if self.__lnaddr is None:
self.__lnaddr = lndecode(self.lightning_invoice)
return self.__lnaddr
@property
def rhash(self) -> str:
return self._lnaddr.paymenthash.hex()
@classmethod
def from_bech32(cls, invoice: str) -> 'Invoice':
"""Constructs Invoice object from BOLT-11 string.
@@ -259,6 +229,48 @@ class Invoice(StoredObject):
lightning_invoice=None,
)
def get_id(self) -> str:
if self.is_lightning():
return self.rhash
else: # on-chain
return get_id_from_onchain_outputs(outputs=self.get_outputs(), timestamp=self.time)
@attr.s
class Invoice(BaseInvoice):
lightning_invoice = attr.ib(type=str, kw_only=True) # type: Optional[str]
__lnaddr = None
def get_address(self) -> Optional[str]:
"""returns the first address, to be displayed in GUI"""
address = None
if self.outputs:
address = self.outputs[0].address if len(self.outputs) > 0 else None
if not address and self.is_lightning():
address = self._lnaddr.get_fallback_address() or None
return address
@property
def _lnaddr(self) -> LnAddr:
if self.__lnaddr is None:
self.__lnaddr = lndecode(self.lightning_invoice)
return self.__lnaddr
@property
def rhash(self) -> str:
return self._lnaddr.paymenthash.hex()
@lightning_invoice.validator
def _validate_invoice_str(self, attribute, value):
if value is not None:
lnaddr = lndecode(value) # this checks the str can be decoded
self.__lnaddr = lnaddr # save it, just to avoid having to recompute later
def can_be_paid_onchain(self) -> bool:
if self.is_lightning():
return bool(self._lnaddr.get_fallback_address())
else:
return True
def to_debug_json(self) -> Dict[str, Any]:
d = self.to_json()
d.update({
@@ -274,11 +286,24 @@ class Invoice(StoredObject):
d['r_tags'] = [str((a.hex(),b.hex(),c,d,e)) for a,b,c,d,e in ln_routing_info[-1]]
return d
def get_id(self) -> str:
if self.is_lightning():
return self.rhash
else: # on-chain
return get_id_from_onchain_outputs(outputs=self.get_outputs(), timestamp=self.time)
@attr.s
class Request(BaseInvoice):
payment_hash = attr.ib(type=bytes, kw_only=True, converter=hex_to_bytes) # type: Optional[bytes]
def is_lightning(self):
return self.payment_hash is not None
def get_address(self) -> Optional[str]:
"""returns the first address, to be displayed in GUI"""
address = None
if self.outputs:
address = self.outputs[0].address if len(self.outputs) > 0 else None
return address
@property
def rhash(self) -> str:
return self.payment_hash.hex()
def get_id_from_onchain_outputs(outputs: List[PartialTxOutput], *, timestamp: int) -> str: