Merge pull request #10614 from f321x/lnaddr_rename

bolt11: rename *lnaddr* -> *bolt11*
This commit is contained in:
ghost43
2026-04-27 15:31:30 +00:00
committed by GitHub
15 changed files with 116 additions and 126 deletions
+3 -3
View File
@@ -7,7 +7,7 @@ from typing import Optional
from . import bitcoin
from .util import format_satoshis_plain
from .bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
from .lnaddr import lndecode, LnDecodeException
from .bolt11 import decode_bolt11_invoice, BOLT11DecodeException
# note: when checking against these, use .lower() to support case-insensitivity
BITCOIN_BIP21_URI_SCHEME = 'bitcoin'
@@ -93,8 +93,8 @@ def parse_bip21_URI(uri: str) -> dict:
raise InvalidBitcoinURI(f"failed to parse 'sig' field: {repr(e)}") from e
if 'lightning' in out:
try:
lnaddr = lndecode(out['lightning'])
except LnDecodeException as e:
lnaddr = decode_bolt11_invoice(out['lightning'])
except BOLT11DecodeException as e:
raise InvalidBitcoinURI(f"Failed to decode 'lightning' field: {e!r}") from e
amount_sat = out.get('amount')
if amount_sat:
+25 -24
View File
@@ -23,9 +23,9 @@ if TYPE_CHECKING:
from .lnutil import LnFeatures
class LnInvoiceException(Exception): pass
class LnDecodeException(LnInvoiceException): pass
class LnEncodeException(LnInvoiceException): pass
class BOLT11InvoiceException(Exception): pass
class BOLT11DecodeException(BOLT11InvoiceException): pass
class BOLT11EncodeException(BOLT11InvoiceException): pass
# BOLT #11:
@@ -68,7 +68,7 @@ def unshorten_amount(amount) -> Decimal:
# A reader SHOULD fail if `amount` contains a non-digit, or is followed by
# anything except a `multiplier` in the table above.
if not re.fullmatch("\\d+[pnum]?", str(amount)):
raise LnDecodeException("Invalid amount '{}'".format(amount))
raise BOLT11DecodeException("Invalid amount '{}'".format(amount))
if unit in units.keys():
return Decimal(amount[:-1]) / units[unit]
@@ -88,7 +88,7 @@ def encode_fallback_addr(fallback: str, net: Type[AbstractNet]) -> Sequence[int]
elif addrtype == net.ADDRTYPE_P2SH:
wver = 18
else:
raise LnEncodeException(f"Unknown address type {addrtype} for {net}")
raise BOLT11EncodeException(f"Unknown address type {addrtype} for {net}")
wprog = addr
data5 = convertbits(wprog, 8, 5)
assert data5 is not None
@@ -156,7 +156,7 @@ def pull_tagged(data5: bytearray) -> Tuple[str, Sequence[int]]:
return ret
def lnencode(addr: 'LnAddr', privkey) -> str:
def encode_bolt11_invoice(addr: 'BOLT11Addr', privkey) -> str:
if addr.amount:
amount = addr.net.BOLT11_HRP + shorten_amount(addr.amount)
else:
@@ -185,7 +185,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
# A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields,
if k in ('d', 'h', 'n', 'x', 'p', 's', '9'):
if k in tags_set:
raise LnEncodeException("Duplicate '{}' tag".format(k))
raise BOLT11EncodeException("Duplicate '{}' tag".format(k))
if k == 'r':
route = bytearray()
@@ -228,7 +228,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
data5 += tagged5('9', feature_bits)
else:
# FIXME: Support unknown tags?
raise LnEncodeException("Unknown tag {}".format(k))
raise BOLT11EncodeException("Unknown tag {}".format(k))
tags_set.add(k)
@@ -254,7 +254,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
return bech32_encode(segwit_addr.Encoding.BECH32, hrp, data5)
class LnAddr(object):
class BOLT11Addr:
def __init__(self, *, paymenthash: bytes = None, amount=None, net: Type[AbstractNet] = None, tags=None, date=None,
payment_secret: bytes = None):
self.date = int(time.time()) if not date else int(date)
@@ -274,16 +274,16 @@ class LnAddr(object):
@amount.setter
def amount(self, value):
if not (isinstance(value, Decimal) or value is None):
raise LnInvoiceException(f"amount must be Decimal or None, not {value!r}")
raise BOLT11InvoiceException(f"amount must be Decimal or None, not {value!r}")
if value is None:
self._amount = None
return
assert isinstance(value, Decimal)
if value.is_nan() or not (0 <= value <= TOTAL_COIN_SUPPLY_LIMIT_IN_BTC):
raise LnInvoiceException(f"amount is out-of-bounds: {value!r} BTC")
raise BOLT11InvoiceException(f"amount is out-of-bounds: {value!r} BTC")
if value * 10**12 % 10:
# max resolution is millisatoshi
raise LnInvoiceException(f"Cannot encode {value!r}: too many decimal places")
raise BOLT11InvoiceException(f"Cannot encode {value!r}: too many decimal places")
self._amount = value
def get_amount_sat(self) -> Optional[Decimal]:
@@ -332,7 +332,8 @@ class LnAddr(object):
def validate_and_compare_features(self, myfeatures: 'LnFeatures') -> None:
"""Raises IncompatibleOrInsaneFeatures.
note: these checks are not done by the parser (in lndecode), as then when we started requiring a new feature,
note: these checks are not done by the parser (in decode_bolt11_invoice),
as then when we started requiring a new feature,
old saved already paid invoices could no longer be parsed.
"""
from .lnutil import validate_features, ln_compare_features
@@ -341,7 +342,7 @@ class LnAddr(object):
ln_compare_features(myfeatures.for_invoice(), invoice_features)
def __str__(self):
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
return "BOLT11Addr[{}, amount={}{} tags=[{}]]".format(
hexlify(self.pubkey.serialize()).decode('utf-8') if self.pubkey else None,
self.amount, self.net.BOLT11_HRP,
", ".join([k + '=' + str(v) for k, v in self.tags])
@@ -403,9 +404,9 @@ class SerializableKey:
return self.pubkey.get_public_key_bytes(True)
def lndecode(invoice: str, *, verbose=False, net=None) -> LnAddr:
"""Parses a string into an LnAddr object.
Can raise LnDecodeException or IncompatibleOrInsaneFeatures.
def decode_bolt11_invoice(invoice: str, *, verbose=False, net=None) -> BOLT11Addr:
"""Parses a string into a BOLT11Addr object.
Can raise BOLT11DecodeException or IncompatibleOrInsaneFeatures.
"""
if net is None:
net = constants.net
@@ -413,27 +414,27 @@ def lndecode(invoice: str, *, verbose=False, net=None) -> LnAddr:
hrp = decoded_bech32.hrp
data5 = decoded_bech32.data # "5" as in list of 5-bit integers
if decoded_bech32.encoding is None:
raise LnDecodeException("Bad bech32 checksum")
raise BOLT11DecodeException("Bad bech32 checksum")
if decoded_bech32.encoding != segwit_addr.Encoding.BECH32:
raise LnDecodeException("Bad bech32 encoding: must be using vanilla BECH32")
raise BOLT11DecodeException("Bad bech32 encoding: must be using vanilla BECH32")
# BOLT #11:
#
# A reader MUST fail if it does not understand the `prefix`.
if not hrp.startswith('ln'):
raise LnDecodeException("Does not start with ln")
raise BOLT11DecodeException("Does not start with ln")
if not hrp[2:].startswith(net.BOLT11_HRP):
raise LnDecodeException(f"Wrong Lightning invoice HRP {hrp[2:]}, should be {net.BOLT11_HRP}")
raise BOLT11DecodeException(f"Wrong Lightning invoice HRP {hrp[2:]}, should be {net.BOLT11_HRP}")
# Final signature 65 bytes, split it off.
if len(data5) < 65*8//5:
raise LnDecodeException("Too short to contain signature")
raise BOLT11DecodeException("Too short to contain signature")
sigdecoded = bytes(convertbits(data5[-65*8//5:], 5, 8, False))
data5 = data5[:-65*8//5]
data5_remaining = bytearray(data5) # note: bytearray is faster than list of ints
addr = LnAddr()
addr = BOLT11Addr()
addr.pubkey = None
addr.net = net
@@ -580,7 +581,7 @@ def lndecode(invoice: str, *, verbose=False, net=None) -> LnAddr:
# A reader MUST use the `n` field to validate the signature instead of
# performing signature recovery if a valid `n` field is provided.
if not ecc.ECPubkey(addr.pubkey).ecdsa_verify(sigdecoded[:64], hrp_hash):
raise LnDecodeException("bad signature")
raise BOLT11DecodeException("bad signature")
pubkey_copy = addr.pubkey
class WrappedBytesKey:
+4 -8
View File
@@ -3,16 +3,12 @@ from typing import TYPE_CHECKING, Sequence
import PyQt6.QtGui as QtGui
import PyQt6.QtWidgets as QtWidgets
import PyQt6.QtCore as QtCore
from PyQt6.QtWidgets import QLabel, QLineEdit, QHBoxLayout, QGridLayout
from PyQt6.QtWidgets import QLabel, QHBoxLayout
from electrum.util import EventListener, ShortID
from electrum.util import ShortID
from electrum.i18n import _
from electrum.util import format_time
from electrum.lnutil import format_short_channel_id, LOCAL, REMOTE, UpdateAddHtlc, Direction
from electrum.lnchannel import htlcsum, Channel, AbstractChannel, HTLCWithStatus
from electrum.lnaddr import LnAddr, lndecode
from electrum.bitcoin import COIN
from electrum.wallet import Abstract_Wallet
from electrum.lnutil import LOCAL, REMOTE, UpdateAddHtlc, Direction
from electrum.lnchannel import Channel, AbstractChannel, HTLCWithStatus
from electrum.gui.common_qt.util import QtEventListener, qt_event_listener
from .util import Buttons, CloseButton, ShowQRLineEdit, MessageBoxMixin, WWLabel, VLine
+3 -3
View File
@@ -73,7 +73,7 @@ from electrum.exchange_rate import FxThread
from electrum.simple_config import SimpleConfig
from electrum.logging import Logger
from electrum.lntransport import extract_nodeid, ConnStringFormatError
from electrum.lnaddr import lndecode, LnAddr
from electrum.bolt11 import decode_bolt11_invoice, BOLT11Addr
from electrum.submarine_swaps import SwapServerTransport, NostrTransport
from electrum.fee_policy import FeePolicy
@@ -1678,7 +1678,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
def show_lightning_invoice(self, invoice: Invoice):
from electrum.util import format_short_id
lnaddr = lndecode(invoice.lightning_invoice)
lnaddr = decode_bolt11_invoice(invoice.lightning_invoice)
d = WindowModalDialog(self, _("Lightning Invoice"))
vbox = QVBoxLayout(d)
grid = QGridLayout()
@@ -1713,7 +1713,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
grid.addWidget(QLabel(_('Text') + ':'), 8, 0)
grid.addWidget(invoice_e, 8, 1)
r_tags = lnaddr.get_routing_info('r')
r_tags = '\n'.join(repr(r) for r in LnAddr.format_bolt11_routing_info_as_human_readable(r_tags))
r_tags = '\n'.join(repr(r) for r in BOLT11Addr.format_bolt11_routing_info_as_human_readable(r_tags))
routing_e = QTextEdit(str(r_tags))
routing_e.setReadOnly(True)
grid.addWidget(QLabel(_("Routing Hints") + ':'), 9, 0)
+2 -2
View File
@@ -50,9 +50,9 @@ def parse_bip21(text):
def parse_bolt11(text):
from electrum.lnaddr import lndecode
from electrum.bolt11 import decode_bolt11_invoice
try:
return lndecode(text)
return decode_bolt11_invoice(text)
except Exception:
return
+5 -5
View File
@@ -9,7 +9,7 @@ from .i18n import _
from .util import age, InvoiceError, format_satoshis
from .bip21 import create_bip21_uri
from .lnutil import hex_to_bytes
from .lnaddr import lndecode, LnAddr
from .bolt11 import decode_bolt11_invoice, BOLT11Addr
from . import constants
from .bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
from .bitcoin import address_to_script
@@ -213,7 +213,7 @@ class BaseInvoice(StoredObject):
Might raise InvoiceError.
"""
try:
lnaddr = lndecode(invoice)
lnaddr = decode_bolt11_invoice(invoice)
except Exception as e:
raise InvoiceError(e) from e
amount_msat = lnaddr.get_amount_msat()
@@ -275,9 +275,9 @@ class Invoice(BaseInvoice):
return address
@property
def _lnaddr(self) -> LnAddr:
def _lnaddr(self) -> BOLT11Addr:
if self.__lnaddr is None:
self.__lnaddr = lndecode(self.lightning_invoice)
self.__lnaddr = decode_bolt11_invoice(self.lightning_invoice)
return self.__lnaddr
@property
@@ -288,7 +288,7 @@ class Invoice(BaseInvoice):
@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
lnaddr = decode_bolt11_invoice(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:
+5 -5
View File
@@ -12,7 +12,7 @@ import aiohttp.client_exceptions
from electrum import segwit_addr, util
from electrum.segwit_addr import bech32_decode, Encoding, convertbits, bech32_encode
from electrum.lnaddr import LnDecodeException, LnEncodeException
from electrum.bolt11 import BOLT11DecodeException, BOLT11EncodeException
from electrum.network import Network
from electrum.logging import get_logger
from electrum.i18n import _
@@ -47,11 +47,11 @@ def decode_lnurl(lnurl: str) -> str:
hrp = decoded_bech32.hrp
data = decoded_bech32.data
if decoded_bech32.encoding is None:
raise LnDecodeException("Bad bech32 checksum")
raise BOLT11DecodeException("Bad bech32 checksum")
if decoded_bech32.encoding != Encoding.BECH32:
raise LnDecodeException("Bad bech32 encoding: must be using vanilla BECH32")
raise BOLT11DecodeException("Bad bech32 encoding: must be using vanilla BECH32")
if not hrp.startswith("lnurl"):
raise LnDecodeException("Does not start with lnurl")
raise BOLT11DecodeException("Does not start with lnurl")
data = convertbits(data, 5, 8, False)
url = bytes(data).decode("utf-8")
return url
@@ -62,7 +62,7 @@ def encode_lnurl(url: str) -> str:
try:
url = url.encode("utf-8")
except UnicodeError as e:
raise LnEncodeException("invalid url") from e
raise BOLT11EncodeException("invalid url") from e
bech32_data = convertbits(url, 8, 5, True)
assert bech32_data
lnurl = bech32_encode(
+11 -11
View File
@@ -64,7 +64,7 @@ from .lntransport import (
ConnStringFormatError
)
from .lnpeer import Peer, LN_P2P_NETWORK_TIMEOUT
from .lnaddr import lnencode, LnAddr, lndecode
from .bolt11 import encode_bolt11_invoice, BOLT11Addr, decode_bolt11_invoice
from .lnchannel import Channel, AbstractChannel, ChannelState, PeerState, HTLCWithStatus, ChannelBackup
from .lnrater import LNRater
from .lnutil import (
@@ -123,7 +123,7 @@ class PaymentInfo:
- Historically, we used to store "bolt11, direction, status", but deserializing bolt11 was too slow.
(even deserializing just once - all bolt11 during wallet-open - was slow)
- note: the deserialization code in lnaddr.py has been significantly sped up since
- note: the deserialization code in bolt11.py has been significantly sped up since
- For incoming payments, for unpaid requests, ~every time the user displays the unpaid bolt11,
we get a chance to display a new bolt11, with same payment_hash/amount, but with updated
routing_hints (channels might get closed/opened, or just liquidity changed drastically).
@@ -1930,7 +1930,7 @@ class LNWallet(Logger):
f"pay_to_node starting session for RHASH={payment_hash.hex()}. "
f"using_trampoline={self.uses_trampoline()}. "
f"invoice_features={paysession.invoice_features.get_names()}. "
f"r_tags={LnAddr.format_bolt11_routing_info_as_human_readable(r_tags)}. "
f"r_tags={BOLT11Addr.format_bolt11_routing_info_as_human_readable(r_tags)}. "
f"{amount_to_pay=} msat. {budget=}")
if not self.uses_trampoline():
self.logger.info(
@@ -2219,11 +2219,11 @@ class LNWallet(Logger):
except Exception:
return None
def _check_bolt11_invoice(self, bolt11_invoice: str, *, amount_msat: int = None) -> LnAddr:
"""Parses and validates a bolt11 invoice str into a LnAddr.
def _check_bolt11_invoice(self, bolt11_invoice: str, *, amount_msat: int = None) -> BOLT11Addr:
"""Parses and validates a bolt11 invoice str into a BOLT11Addr.
Includes pre-payment checks external to the parser.
"""
addr = lndecode(bolt11_invoice)
addr = decode_bolt11_invoice(bolt11_invoice)
if addr.is_expired():
raise InvoiceError(_("This invoice has expired"))
# check amount
@@ -2563,7 +2563,7 @@ class LNWallet(Logger):
message: str,
fallback_address: Optional[str],
channels: Optional[Sequence[Channel]] = None,
) -> Tuple[LnAddr, str]:
) -> Tuple[BOLT11Addr, str]:
amount_msat = payment_info.amount_msat
pair = self._bolt11_cache.get(payment_info.payment_hash)
if pair:
@@ -2580,12 +2580,12 @@ class LNWallet(Logger):
# TODO: make invoice_features dynamic depending on available trampoline channels
only_trampoline=payment_info.invoice_features.supports(LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM),
)
formatted_r_hints = LnAddr.format_bolt11_routing_info_as_human_readable(routing_hints, has_explicit_r_tagtype=True)
formatted_r_hints = BOLT11Addr.format_bolt11_routing_info_as_human_readable(routing_hints, has_explicit_r_tagtype=True)
self.logger.info(f"creating bolt11 invoice with routing_hints: {formatted_r_hints}, sat: {(amount_msat or 0) // 1000}")
payment_secret = self.get_payment_secret(payment_info.payment_hash)
amount_btc = amount_msat/Decimal(COIN*1000) if amount_msat else None
min_final_cltv_delta = payment_info.min_final_cltv_delta + MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE
lnaddr = LnAddr(
lnaddr = BOLT11Addr(
paymenthash=payment_info.payment_hash,
amount=amount_btc,
tags=[
@@ -2597,7 +2597,7 @@ class LNWallet(Logger):
] + routing_hints,
date=timestamp,
payment_secret=payment_secret)
invoice = lnencode(lnaddr, self.node_keypair.privkey)
invoice = encode_bolt11_invoice(lnaddr, self.node_keypair.privkey)
pair = lnaddr, invoice
self._bolt11_cache[payment_info.payment_hash] = pair
return pair
@@ -3971,7 +3971,7 @@ class LNWallet(Logger):
invoice_features = payload["invoice_features"]["invoice_features"]
invoice_routing_info = payload["invoice_routing_info"]["invoice_routing_info"]
r_tags = decode_routing_info(invoice_routing_info)
self.logger.info(f'r_tags {LnAddr.format_bolt11_routing_info_as_human_readable(r_tags)}')
self.logger.info(f'r_tags {BOLT11Addr.format_bolt11_routing_info_as_human_readable(r_tags)}')
# TODO legacy mpp payment, use total_msat from trampoline onion
else:
self.logger.info('forward_trampoline: end-to-end')
+2 -2
View File
@@ -18,7 +18,7 @@ from .lnurl import (decode_lnurl, request_lnurl, callback_lnurl, LNURLError,
lightning_address_to_url, try_resolve_lnurlpay, LNURL6Data,
LNURL3Data, LNURLData, SUPPORTED_LNURL_SCHEMES)
from .bitcoin import opcodes, construct_script
from .lnaddr import LnInvoiceException
from .bolt11 import BOLT11InvoiceException
from .lnutil import IncompatibleOrInsaneFeatures
from .bip21 import parse_bip21_URI, InvalidBitcoinURI, LIGHTNING_URI_SCHEME, BITCOIN_BIP21_URI_SCHEME
from .segwit_addr import bech32_decode
@@ -520,7 +520,7 @@ class PaymentIdentifier(Logger):
error = _("Error parsing Lightning invoice") + f":\n{e!r}"
if e.args and len(e.args):
arg = e.args[0]
if isinstance(arg, LnInvoiceException):
if isinstance(arg, BOLT11InvoiceException):
error = _("Error parsing Lightning invoice") + f":\n{e}"
elif isinstance(arg, IncompatibleOrInsaneFeatures):
error = _("Invoice requires unknown or incompatible Lightning feature") + f":\n{e!r}"
-7
View File
@@ -1,14 +1,7 @@
import os
import asyncio
from collections import defaultdict
from typing import TYPE_CHECKING
from aiohttp import web
from electrum.util import log_exceptions, ignore_exceptions
from electrum.logging import Logger
from electrum.util import EventListener
from electrum.lnaddr import lndecode
from electrum.daemon import AuthenticatedServer
+2 -2
View File
@@ -38,7 +38,7 @@ from .util import (
)
from . import lnutil
from .lnutil import hex_to_bytes, REDEEM_AFTER_DOUBLE_SPENT_DELAY, Keypair
from .lnaddr import lndecode
from .bolt11 import decode_bolt11_invoice
from .json_db import StoredObject, stored_in
from . import constants
from .address_synchronizer import (TX_HEIGHT_LOCAL, TX_HEIGHT_FUTURE, TX_HEIGHT_UNCONFIRMED,
@@ -997,7 +997,7 @@ class SwapManager(Logger):
}
await transport.send_request_to_server('addswapinvoice', request_data)
# wait for funding tx
lnaddr = lndecode(invoice)
lnaddr = decode_bolt11_invoice(invoice)
while swap.funding_txid is None and not lnaddr.is_expired():
await asyncio.sleep(0.1)
return swap.funding_txid
+6 -6
View File
@@ -905,7 +905,7 @@ class WalletDBUpgrader(Logger):
self.data['seed_version'] = 44
def _convert_version_45(self):
from .lnaddr import lndecode
from .bolt11 import decode_bolt11_invoice
if not self._is_upgrade_method_needed(44, 44):
return
swaps = self.data.get('submarine_swaps', {})
@@ -921,7 +921,7 @@ class WalletDBUpgrader(Logger):
outputs = item['outputs'] if not is_lightning else None
bip70 = item['bip70'] if not is_lightning else None
if is_lightning:
lnaddr = lndecode(item['invoice'])
lnaddr = decode_bolt11_invoice(item['invoice'])
amount_msat = lnaddr.get_amount_msat()
timestamp = lnaddr.date
exp_delay = lnaddr.get_expiry()
@@ -974,7 +974,7 @@ class WalletDBUpgrader(Logger):
self.data['seed_version'] = 46
def _convert_version_47(self):
from .lnaddr import lndecode
from .bolt11 import decode_bolt11_invoice
if not self._is_upgrade_method_needed(46, 46):
return
# recalc keys of requests
@@ -982,7 +982,7 @@ class WalletDBUpgrader(Logger):
for key, item in list(requests.items()):
lnaddr = item.get('lightning_invoice')
if lnaddr:
lnaddr = lndecode(lnaddr)
lnaddr = decode_bolt11_invoice(lnaddr)
rhash = lnaddr.paymenthash.hex()
if key != rhash:
requests[rhash] = item
@@ -1023,7 +1023,7 @@ class WalletDBUpgrader(Logger):
self.data['seed_version'] = 50
def _convert_version_51(self):
from .lnaddr import lndecode
from .bolt11 import decode_bolt11_invoice
if not self._is_upgrade_method_needed(50, 50):
return
requests = self.data.get('payment_requests', {})
@@ -1032,7 +1032,7 @@ class WalletDBUpgrader(Logger):
if lightning_invoice is None:
payment_hash = None
else:
lnaddr = lndecode(lightning_invoice)
lnaddr = decode_bolt11_invoice(lightning_invoice)
payment_hash = lnaddr.paymenthash.hex()
item['payment_hash'] = payment_hash
self.data['seed_version'] = 51
+35 -35
View File
@@ -4,7 +4,7 @@ from binascii import unhexlify, hexlify
import pprint
import unittest
from electrum.lnaddr import shorten_amount, unshorten_amount, LnAddr, lnencode, lndecode
from electrum.bolt11 import shorten_amount, unshorten_amount, BOLT11Addr, encode_bolt11_invoice, decode_bolt11_invoice
from electrum.segwit_addr import bech32_encode, bech32_decode
from electrum import segwit_addr
from electrum.lnutil import UnknownEvenFeatureBits, LnFeatures, IncompatibleLightningFeatures
@@ -67,106 +67,106 @@ class TestBolt11(ElectrumTestCase):
timestamp = 1615922274
tests = [
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, tags=[('d', ''), ('9', 33282)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, tags=[('d', ''), ('9', 33282)]),
"lnbc1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdqq9qypqszpyrpe4tym8d3q87d43cgdhhlsrt78epu7u99mkzttmt2wtsx0304rrw50addkryfrd3vn3zy467vxwlmf4uz7yvntuwjr2hqjl9lw5cqwtp2dy"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60), ('9', 0x28200)]),
"lnbc1m1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5xysxxatsyp3k7enxv4jsxqzpu9qy9qsqw8l2pulslacwjt86vle3sgfdmcct5v34gtcpfnujsf6ufqa7v7jzdpddnwgte82wkscdlwfwucrgn8z36rv9hzk5mukltteh0yqephqpk5vegu"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=Decimal('1'), tags=[('h', longdescription), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=Decimal('1'), tags=[('h', longdescription), ('9', 0x28200)]),
"lnbc11ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsq0jnua6dc4p984aeafs6ss7tjjj7553ympvg82qrjq0zgdqgtdvt5wlwkvw4ds5sn96nazp6ct9ts37tcw708kzkk4p8znahpsgp9tnspnycsf7"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, net=constants.BitcoinTestnet, tags=[('f', 'mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'), ('h', longdescription), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, net=constants.BitcoinTestnet, tags=[('f', 'mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'), ('h', longdescription), ('9', 0x28200)]),
"lntb1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfpp3x9et2e20v6pu37c5d9vax37wxq72un98hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqy5826t0z3sn29z396pmr4kv73lcx0v7y6vas6h3pysmqllmzwgm5ps2t468gm4psj52usjy6y4xcry4k84n2zggs6f9agwg95454v6gqrwmh4f"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[
('r', [(unhexlify('029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'), unhexlify('0102030405060708'), 1, 20, 3),
(unhexlify('039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'), unhexlify('030405060708090a'), 2, 30, 4)]),
('f', '1RustyRX2oai4EYYDpQGWvEL62BBGqN9T'),
('h', longdescription),
('9', 0x28200)]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqfnk063vsrgjx7l6td6v42skuxql7epn5tmrl4qte2e78nqnsjlgjg3sgkxreqex5fw4c9chnvtc2hykqnyxr84zwfr8f3d9q3h0nfdgqenlzvj"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('f', '3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX'), ('h', longdescription), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('f', '3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX'), ('h', longdescription), ('9', 0x28200)]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfppj3a24vwu6r8ejrss3axul8rxldph2q7z9hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqqf6z4r7ruzr5txm5ln4netwa2f4x233tud7jy8gxrynyx07rxt7qm92yk2krlgwr7d8jknglur75sujeyapmda5nf3femrk2mep8a2cp4hlvup"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('f', 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'), ('h', longdescription), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('f', 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'), ('h', longdescription), ('9', 0x28200)]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfppqw508d6qejxtdg4y5r3zarvary0c5xw7khp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqy4wp73jma5uktd9y7yha56f98n2k0hxgnvp2qdcury00dapps3k3urgfy8tvv8jzwcafpy576msk5xx2hladf06m3s5mgx5msn4elfqqaaqjhk"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('f', 'bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3'), ('h', longdescription), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('f', 'bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3'), ('h', longdescription), ('9', 0x28200)]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqgt4gg9uktlpgnnuvczazusp5uwjv78na305ucsw06c8uk58e5stjqj9sz7fgavw0z688alt364js72mc9mg8yumhpes2dsmq5k9nr5qqddykxy"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('n', PUBKEY), ('h', longdescription), ('9', 0x28200)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('n', PUBKEY), ('h', longdescription), ('9', 0x28200)]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsq2y235rxw7v0gkn2t9ehc742tm3p22q2yjjykq4d85ze6g62yk60navxqz0ga96sqrszju8nlfajthem4gngxvyz4hwy39j4nqm8kv0qq9znxs7"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 2 + (1 << 9) + (1 << 15))]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 2 + (1 << 9) + (1 << 15))]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqszrwfgrl5k3rt4q4mclc8t00p2tcjsf9pmpcq6lu5zhmampyvk43fk30eqpdm8t5qmdpzan25aqxqaqdzmy0smrtduazjcxx975vz78ccpx0qhev"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 8) + (1 << 15))]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 8) + (1 << 15))]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqg2wans8f6vkfd3l7zjv547hlc7wd7eqyxfwhtdudnkkgrpk6p9ffykwrvdtwm0aakaxujurdxgd7cllnfypmj22cvy7z333udg6zncgacqzmd2z9"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9) + (1 << 15))]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9) + (1 << 15))]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqs2dr525u5f4kjxdv0hq5c822qwxrtttjl4u586yl84x0kvvx66gz9ygy76005s5sjwgr7fp55ccsae47vpl4gqvwhc3exps964g743j5gqwtt68t"),
(LnAddr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9) + (1 << 14))]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9) + (1 << 14))]),
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrss2f8kr98446xls02yndup2ynwjh46u8kdeuuncexx2hnets0j0064nyq25gkd6jnttldzt5qqtszum5dufvuvryxt204w2p24557udxgcp0nlwtw"),
]
# Some old tests follow that do not have payment_secret. Note that if the parser raised due to the lack of features/payment_secret,
# old wallets that have these invoices saved (as paid/expired), could not be opened (though we could do a db upgrade and delete them).
tests.extend([
(LnAddr(date=timestamp, paymenthash=RHASH, tags=[('d', '')]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, tags=[('d', '')]),
"lnbc1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqd9n3kwjjwglnfne5p4rvkze998m3xcxrc8kunl5khkchlaqhwhlyztuuwkrglv47mqg96mcqjjx70hh9luaj4te0u4ww6aclxwve3fqpkmdxlj"),
(LnAddr(date=timestamp, paymenthash=RHASH, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60)]),
"lnbc1m1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9rflz25dx0qw6kdg05u0c5hdc30yq6ga6ew4pz86n244va45nchns9zrs3wjxznsqnt37hz7pswvc56wvuhxcjyd6k3lqf4ujynyxuspmvr078"),
(LnAddr(date=timestamp, paymenthash=RHASH, amount=Decimal('1'), tags=[('h', longdescription)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, amount=Decimal('1'), tags=[('h', longdescription)]),
"lnbc11ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs2qjafckq94q3js6lvqz2kmenn9ysjejyj8fm4hlx0xtqhaxfzlxjappkgp0hmm40dnuan4v3jy83lqjup2n0fdzgysg049y9l9uc98qq07kfd3"),
(LnAddr(date=timestamp, paymenthash=RHASH, net=constants.BitcoinTestnet, tags=[('f', 'mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'), ('h', longdescription)]),
(BOLT11Addr(date=timestamp, paymenthash=RHASH, net=constants.BitcoinTestnet, tags=[('f', 'mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'), ('h', longdescription)]),
"lntb1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsr9zktgu78k8p9t8555ve37qwfvqn6ga37fnfwhgexmf20nzdpmuhwvuv7zra3xrh8y2ggxxuemqfsgka9x7uzsrcx8rfv85c8pmhq9gq4sampn"),
])
# Roundtrip
for lnaddr1, invoice_str1 in tests:
invoice_str2 = lnencode(lnaddr1, PRIVKEY)
invoice_str2 = encode_bolt11_invoice(lnaddr1, PRIVKEY)
self.assertEqual(invoice_str1, invoice_str2)
lnaddr2 = lndecode(invoice_str2, net=lnaddr1.net)
lnaddr2 = decode_bolt11_invoice(invoice_str2, net=lnaddr1.net)
self.compare(lnaddr1, lnaddr2)
def test_n_decoding(self):
# We flip the signature recovery bit, which would normally give a different
# pubkey.
_, hrp, data = bech32_decode(
lnencode(LnAddr(paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('d', ''), ('9', 33282)]), PRIVKEY),
encode_bolt11_invoice(BOLT11Addr(paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('d', ''), ('9', 33282)]), PRIVKEY),
ignore_long_length=True)
data[-1] ^= 1
lnaddr = lndecode(bech32_encode(segwit_addr.Encoding.BECH32, hrp, data), verbose=True)
lnaddr = decode_bolt11_invoice(bech32_encode(segwit_addr.Encoding.BECH32, hrp, data), verbose=True)
self.assertNotEqual(lnaddr.pubkey.serialize(), PUBKEY)
# But not if we supply expliciy `n` specifier!
_, hrp, data = bech32_decode(
lnencode(LnAddr(paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('d', ''), ('n', PUBKEY), ('9', 33282)]), PRIVKEY),
encode_bolt11_invoice(BOLT11Addr(paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('d', ''), ('n', PUBKEY), ('9', 33282)]), PRIVKEY),
ignore_long_length=True)
data[-1] ^= 1
lnaddr = lndecode(bech32_encode(segwit_addr.Encoding.BECH32, hrp, data), verbose=True)
lnaddr = decode_bolt11_invoice(bech32_encode(segwit_addr.Encoding.BECH32, hrp, data), verbose=True)
self.assertEqual(lnaddr.pubkey.serialize(), PUBKEY)
def test_min_final_cltv_expiry_decoding(self):
lnaddr = lndecode("lnsb500u1pdsgyf3pp5nmrqejdsdgs4n9ukgxcp2kcq265yhrxd4k5dyue58rxtp5y83s3qsp5qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsdqqcqzys9qypqsqp2h6a5xeytuc3fad2ed4gxvhd593lwjdna3dxsyeem0qkzjx6guk44jend0xq4zzvp6f3fy07wnmxezazzsxgmvqee8shxjuqu2eu0qpnvc95x",
lnaddr = decode_bolt11_invoice("lnsb500u1pdsgyf3pp5nmrqejdsdgs4n9ukgxcp2kcq265yhrxd4k5dyue58rxtp5y83s3qsp5qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsdqqcqzys9qypqsqp2h6a5xeytuc3fad2ed4gxvhd593lwjdna3dxsyeem0qkzjx6guk44jend0xq4zzvp6f3fy07wnmxezazzsxgmvqee8shxjuqu2eu0qpnvc95x",
net=constants.BitcoinSimnet)
self.assertEqual(144, lnaddr.get_min_final_cltv_delta())
lnaddr = lndecode("lntb15u1p0m6lzupp5zqjthgvaad9mewmdjuehwddyze9d8zyxcc43zhaddeegt37sndgsdq4xysyymr0vd4kzcmrd9hx7cqp7xqrrss9qy9qsqsp5vlhcs24hwm747w8f3uau2tlrdkvjaglffnsstwyamj84cxuhrn2s8tut3jqumepu42azyyjpgqa4w9w03204zp9h4clk499y2umstl6s29hqyj8vv4as6zt5567ux7l3f66m8pjhk65zjaq2esezk7ll2kcpljewkg",
lnaddr = decode_bolt11_invoice("lntb15u1p0m6lzupp5zqjthgvaad9mewmdjuehwddyze9d8zyxcc43zhaddeegt37sndgsdq4xysyymr0vd4kzcmrd9hx7cqp7xqrrss9qy9qsqsp5vlhcs24hwm747w8f3uau2tlrdkvjaglffnsstwyamj84cxuhrn2s8tut3jqumepu42azyyjpgqa4w9w03204zp9h4clk499y2umstl6s29hqyj8vv4as6zt5567ux7l3f66m8pjhk65zjaq2esezk7ll2kcpljewkg",
net=constants.BitcoinTestnet)
self.assertEqual(30, lnaddr.get_min_final_cltv_delta())
def test_min_final_cltv_expiry_roundtrip(self):
for cltv in (1, 15, 16, 31, 32, 33, 150, 511, 512, 513, 1023, 1024, 1025):
lnaddr = LnAddr(
lnaddr = BOLT11Addr(
paymenthash=RHASH, payment_secret=b"\x01"*32, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60), ('c', cltv), ('9', 33282)])
self.assertEqual(cltv, lnaddr.get_min_final_cltv_delta())
invoice = lnencode(lnaddr, PRIVKEY)
self.assertEqual(cltv, lndecode(invoice).get_min_final_cltv_delta())
invoice = encode_bolt11_invoice(lnaddr, PRIVKEY)
self.assertEqual(cltv, decode_bolt11_invoice(invoice).get_min_final_cltv_delta())
def test_features(self):
lnaddr = lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9qypqsztrz5v3jfnxskfv7g8chmyzyrfhf2vupcavuq5rce96kyt6g0zh337h206awccwp335zarqrud4wccgdn39vur44d8um4hmgv06aj0sgpdrv73z")
lnaddr = decode_bolt11_invoice("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9qypqsztrz5v3jfnxskfv7g8chmyzyrfhf2vupcavuq5rce96kyt6g0zh337h206awccwp335zarqrud4wccgdn39vur44d8um4hmgv06aj0sgpdrv73z")
self.assertEqual(33282, lnaddr.get_tag('9'))
self.assertEqual(LnFeatures(33282), lnaddr.get_features())
def test_payment_secret(self):
lnaddr = lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9q5sqqqqqqqqqqqqqqqpqsqvvh7ut50r00p3pg34ea68k7zfw64f8yx9jcdk35lh5ft8qdr8g4r0xzsdcrmcy9hex8un8d8yraewvhqc9l0sh8l0e0yvmtxde2z0hgpzsje5l")
lnaddr = decode_bolt11_invoice("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9q5sqqqqqqqqqqqqqqqpqsqvvh7ut50r00p3pg34ea68k7zfw64f8yx9jcdk35lh5ft8qdr8g4r0xzsdcrmcy9hex8un8d8yraewvhqc9l0sh8l0e0yvmtxde2z0hgpzsje5l")
self.assertEqual((1 << 9) + (1 << 15) + (1 << 99), lnaddr.get_tag('9'))
self.assertEqual(b"\x11" * 32, lnaddr.payment_secret)
def test_validate_and_compare_features(self):
lnaddr = lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9q5sqqqqqqqqqqqqqqqpqsqvvh7ut50r00p3pg34ea68k7zfw64f8yx9jcdk35lh5ft8qdr8g4r0xzsdcrmcy9hex8un8d8yraewvhqc9l0sh8l0e0yvmtxde2z0hgpzsje5l")
lnaddr = decode_bolt11_invoice("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9q5sqqqqqqqqqqqqqqqpqsqvvh7ut50r00p3pg34ea68k7zfw64f8yx9jcdk35lh5ft8qdr8g4r0xzsdcrmcy9hex8un8d8yraewvhqc9l0sh8l0e0yvmtxde2z0hgpzsje5l")
lnaddr.validate_and_compare_features(LnFeatures((1 << 8) + (1 << 14) + (1 << 15)))
with self.assertRaises(IncompatibleLightningFeatures):
lnaddr.validate_and_compare_features(LnFeatures((1 << 8) + (1 << 14) + (1 << 16)))
@@ -188,7 +188,7 @@ class TestBolt11(ElectrumTestCase):
'16000000x0x717', 0, 1, 40),]
),
],
LnAddr.format_bolt11_routing_info_as_human_readable(r_tags_expl, has_explicit_r_tagtype=True))
BOLT11Addr.format_bolt11_routing_info_as_human_readable(r_tags_expl, has_explicit_r_tagtype=True))
r_tags_impl = [
[(bfh('029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'), bfh('0102030405060708'), 1, 20, 3),
@@ -204,9 +204,9 @@ class TestBolt11(ElectrumTestCase):
[('038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9',
'16000000x0x717', 0, 1, 40),],
],
LnAddr.format_bolt11_routing_info_as_human_readable(r_tags_impl, has_explicit_r_tagtype=False))
BOLT11Addr.format_bolt11_routing_info_as_human_readable(r_tags_impl, has_explicit_r_tagtype=False))
for has_explicit_r_tagtype in (False, True):
self.assertEqual(
[],
LnAddr.format_bolt11_routing_info_as_human_readable([], has_explicit_r_tagtype=has_explicit_r_tagtype))
BOLT11Addr.format_bolt11_routing_info_as_human_readable([], has_explicit_r_tagtype=has_explicit_r_tagtype))
+2 -2
View File
@@ -20,7 +20,7 @@ from electrum.submarine_swaps import SwapOffer, SwapFees, NostrTransport
from electrum.transaction import Transaction, TxOutput, tx_from_any
from electrum.util import UserFacingException, NotEnoughFunds
from electrum.crypto import sha256
from electrum.lnaddr import lndecode
from electrum.bolt11 import decode_bolt11_invoice
from electrum.daemon import Daemon
from electrum import json_db
@@ -508,7 +508,7 @@ class TestCommandsTestnet(ElectrumTestCase):
expiry=3500,
wallet=wallet,
)
invoice = lndecode(invoice=result['invoice'])
invoice = decode_bolt11_invoice(invoice=result['invoice'])
assert invoice.paymenthash.hex() == payment_hash
assert wallet.lnworker.get_payment_info(bytes.fromhex(payment_hash), direction=RECEIVED)
assert payment_hash in wallet.lnworker.dont_expire_htlcs
+9 -9
View File
@@ -29,7 +29,7 @@ from electrum import constants
from electrum import bip32
from electrum.network import Network, ProxySettings
from electrum import simple_config, lnutil
from electrum.lnaddr import lnencode, LnAddr, lndecode
from electrum.bolt11 import encode_bolt11_invoice, BOLT11Addr, decode_bolt11_invoice
from electrum.bitcoin import COIN, sha256
from electrum.transaction import Transaction
from electrum.util import NetworkRetryManager, bfh, OldTaskGroup, EventListener, InvoiceError
@@ -179,7 +179,7 @@ class MockLNWallet(LNWallet):
self.channel_db.stop()
await self.channel_db.stopped_event.wait()
async def create_routes_from_invoice(self, amount_msat: int, decoded_invoice: LnAddr, *, full_path=None):
async def create_routes_from_invoice(self, amount_msat: int, decoded_invoice: BOLT11Addr, *, full_path=None):
paysession = PaySession(
payment_hash=decoded_invoice.paymenthash,
payment_secret=decoded_invoice.payment_secret,
@@ -419,7 +419,7 @@ class TestPeer(ElectrumTestCase):
invoice_features: LnFeatures = None,
min_final_cltv_delta: int = None,
expiry: int = None,
) -> Tuple[LnAddr, Invoice]:
) -> Tuple[BOLT11Addr, Invoice]:
amount_btc = amount_msat/Decimal(COIN*1000)
if payment_preimage is None and not payment_hash:
payment_preimage = os.urandom(32)
@@ -450,7 +450,7 @@ class TestPeer(ElectrumTestCase):
invoice_features=invoice_features,
)
w2.save_payment_info(info)
lnaddr1 = LnAddr(
lnaddr1 = BOLT11Addr(
paymenthash=payment_hash,
amount=amount_btc,
tags=[
@@ -461,8 +461,8 @@ class TestPeer(ElectrumTestCase):
] + routing_hints,
payment_secret=payment_secret,
)
invoice = lnencode(lnaddr1, w2.node_keypair.privkey)
lnaddr2 = lndecode(invoice) # unlike lnaddr1, this now has a pubkey set
invoice = encode_bolt11_invoice(lnaddr1, w2.node_keypair.privkey)
lnaddr2 = decode_bolt11_invoice(invoice) # unlike lnaddr1, this now has a pubkey set
return lnaddr2, Invoice.from_bech32(invoice)
async def _activate_trampoline(self, w: MockLNWallet):
@@ -1052,7 +1052,7 @@ class TestPeerDirect(TestPeer):
self.assertEqual(PR_UNPAID, w2.get_payment_status(lnaddr.paymenthash, direction=RECEIVED))
assert lnaddr.get_min_final_cltv_delta() == 400 # what the receiver expects
lnaddr.tags = [tag for tag in lnaddr.tags if tag[0] != 'c'] + [['c', 144]]
b11 = lnencode(lnaddr, w2.node_keypair.privkey)
b11 = encode_bolt11_invoice(lnaddr, w2.node_keypair.privkey)
pay_req = Invoice.from_bech32(b11)
assert pay_req._lnaddr.get_min_final_cltv_delta() == 144 # what w1 will use to pay
result, log = await w1.pay_invoice(pay_req)
@@ -1095,7 +1095,7 @@ class TestPeerDirect(TestPeer):
# create lightning invoice in the past, so it is expired
with mock.patch('time.time', return_value=int(time.time()) - 10000):
lnaddr, _pay_req = self.prepare_invoice(w2, expiry=3600)
b11 = lnencode(lnaddr, w2.node_keypair.privkey)
b11 = encode_bolt11_invoice(lnaddr, w2.node_keypair.privkey)
pay_req = Invoice.from_bech32(b11)
async def try_pay_expired_invoice(pay_req: Invoice, w1=w1):
@@ -2044,7 +2044,7 @@ class TestPeerDirect(TestPeer):
}
# create 10 invoices (10 pending htlc sets with 1 htlc each)
invoices = [] # type: list[tuple[LnAddr, Invoice]]
invoices = [] # type: list[tuple[BOLT11Addr, Invoice]]
for i in range(10):
lnaddr, pay_req = self.prepare_invoice(bob_w)
# prevent bob from settling so that htlc switch will have to iterate through all pending htlcs