mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
121 lines
3.3 KiB
Python
121 lines
3.3 KiB
Python
import attr
|
|
import time
|
|
|
|
from .json_db import StoredObject
|
|
from .i18n import _
|
|
from .util import age
|
|
from .lnaddr import lndecode
|
|
from . import constants
|
|
from .bitcoin import COIN
|
|
from .transaction import PartialTxOutput
|
|
|
|
# convention: 'invoices' = outgoing , 'request' = incoming
|
|
|
|
# types of payment requests
|
|
PR_TYPE_ONCHAIN = 0
|
|
PR_TYPE_LN = 2
|
|
|
|
# status of payment requests
|
|
PR_UNPAID = 0
|
|
PR_EXPIRED = 1
|
|
PR_UNKNOWN = 2 # sent but not propagated
|
|
PR_PAID = 3 # send and propagated
|
|
PR_INFLIGHT = 4 # unconfirmed
|
|
PR_FAILED = 5
|
|
PR_ROUTING = 6
|
|
|
|
pr_color = {
|
|
PR_UNPAID: (.7, .7, .7, 1),
|
|
PR_PAID: (.2, .9, .2, 1),
|
|
PR_UNKNOWN: (.7, .7, .7, 1),
|
|
PR_EXPIRED: (.9, .2, .2, 1),
|
|
PR_INFLIGHT: (.9, .6, .3, 1),
|
|
PR_FAILED: (.9, .2, .2, 1),
|
|
PR_ROUTING: (.9, .6, .3, 1),
|
|
}
|
|
|
|
pr_tooltips = {
|
|
PR_UNPAID:_('Pending'),
|
|
PR_PAID:_('Paid'),
|
|
PR_UNKNOWN:_('Unknown'),
|
|
PR_EXPIRED:_('Expired'),
|
|
PR_INFLIGHT:_('In progress'),
|
|
PR_FAILED:_('Failed'),
|
|
PR_ROUTING: _('Computing route...'),
|
|
}
|
|
|
|
PR_DEFAULT_EXPIRATION_WHEN_CREATING = 24*60*60 # 1 day
|
|
pr_expiration_values = {
|
|
0: _('Never'),
|
|
10*60: _('10 minutes'),
|
|
60*60: _('1 hour'),
|
|
24*60*60: _('1 day'),
|
|
7*24*60*60: _('1 week'),
|
|
}
|
|
assert PR_DEFAULT_EXPIRATION_WHEN_CREATING in pr_expiration_values
|
|
|
|
outputs_decoder = lambda _list: [PartialTxOutput.from_legacy_tuple(*x) for x in _list]
|
|
|
|
# hack: BOLT-11 is not really clear on what an expiry of 0 means.
|
|
# It probably interprets it as 0 seconds, so already expired...
|
|
# Our higher level invoices code however uses 0 for "never".
|
|
# Hence set some high expiration here
|
|
LN_EXPIRY_NEVER = 100 * 365 * 24 * 60 * 60 # 100 years
|
|
|
|
@attr.s
|
|
class Invoice(StoredObject):
|
|
type = attr.ib(type=int)
|
|
message = attr.ib(type=str)
|
|
amount = attr.ib(type=int)
|
|
exp = attr.ib(type=int)
|
|
time = attr.ib(type=int)
|
|
|
|
def is_lightning(self):
|
|
return self.type == PR_TYPE_LN
|
|
|
|
def get_status_str(self, status):
|
|
status_str = pr_tooltips[status]
|
|
if status == PR_UNPAID:
|
|
if self.exp > 0 and self.exp != LN_EXPIRY_NEVER:
|
|
expiration = self.exp + self.time
|
|
status_str = _('Expires') + ' ' + age(expiration, include_seconds=True)
|
|
else:
|
|
status_str = _('Pending')
|
|
return status_str
|
|
|
|
@attr.s
|
|
class OnchainInvoice(Invoice):
|
|
id = attr.ib(type=str)
|
|
outputs = attr.ib(type=list, converter=outputs_decoder)
|
|
bip70 = attr.ib(type=str) # may be None
|
|
requestor = attr.ib(type=str) # may be None
|
|
|
|
def get_address(self):
|
|
assert len(self.outputs) == 1
|
|
return self.outputs[0].address
|
|
|
|
@attr.s
|
|
class LNInvoice(Invoice):
|
|
rhash = attr.ib(type=str)
|
|
invoice = attr.ib(type=str)
|
|
|
|
@classmethod
|
|
def from_bech32(klass, invoice: str):
|
|
lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
|
|
amount = int(lnaddr.amount * COIN) if lnaddr.amount else None
|
|
return LNInvoice(
|
|
type = PR_TYPE_LN,
|
|
amount = amount,
|
|
message = lnaddr.get_description(),
|
|
time = lnaddr.date,
|
|
exp = lnaddr.get_expiry(),
|
|
rhash = lnaddr.paymenthash.hex(),
|
|
invoice = invoice,
|
|
)
|
|
|
|
|
|
def invoice_from_json(x: dict) -> Invoice:
|
|
if x.get('type') == PR_TYPE_LN:
|
|
return LNInvoice(**x)
|
|
else:
|
|
return OnchainInvoice(**x)
|