diff --git a/electrum/bip21.py b/electrum/bip21.py index c7f3ecfa3..3db495e7b 100644 --- a/electrum/bip21.py +++ b/electrum/bip21.py @@ -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: diff --git a/electrum/lnaddr.py b/electrum/bolt11.py similarity index 92% rename from electrum/lnaddr.py rename to electrum/bolt11.py index 031676840..a532c444e 100644 --- a/electrum/lnaddr.py +++ b/electrum/bolt11.py @@ -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: diff --git a/electrum/gui/qt/channel_details.py b/electrum/gui/qt/channel_details.py index 0bc16527b..42da866d0 100644 --- a/electrum/gui/qt/channel_details.py +++ b/electrum/gui/qt/channel_details.py @@ -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 diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index d6bb67b3d..6e1c35d3e 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -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) diff --git a/electrum/gui/text.py b/electrum/gui/text.py index 09fc64c1e..1c8619310 100644 --- a/electrum/gui/text.py +++ b/electrum/gui/text.py @@ -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 diff --git a/electrum/invoices.py b/electrum/invoices.py index 8f20166cb..c15f32926 100644 --- a/electrum/invoices.py +++ b/electrum/invoices.py @@ -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: diff --git a/electrum/lnurl.py b/electrum/lnurl.py index e08f254d6..603ff201a 100644 --- a/electrum/lnurl.py +++ b/electrum/lnurl.py @@ -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( diff --git a/electrum/lnworker.py b/electrum/lnworker.py index e73fb5126..622f5bcf2 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -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') diff --git a/electrum/payment_identifier.py b/electrum/payment_identifier.py index 198bb68a1..7fe8a2f1e 100644 --- a/electrum/payment_identifier.py +++ b/electrum/payment_identifier.py @@ -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}" diff --git a/electrum/plugins/watchtower/server.py b/electrum/plugins/watchtower/server.py index e18475321..ddc0525ba 100644 --- a/electrum/plugins/watchtower/server.py +++ b/electrum/plugins/watchtower/server.py @@ -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 diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index ef55aa632..37d8d2afe 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -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 diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py index a5d146a00..d00883b91 100644 --- a/electrum/wallet_db.py +++ b/electrum/wallet_db.py @@ -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 diff --git a/tests/test_bolt11.py b/tests/test_bolt11.py index a900545f5..01453a87b 100644 --- a/tests/test_bolt11.py +++ b/tests/test_bolt11.py @@ -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", - net=constants.BitcoinSimnet) + lnaddr = decode_bolt11_invoice("lnsb500u1pdsgyf3pp5nmrqejdsdgs4n9ukgxcp2kcq265yhrxd4k5dyue58rxtp5y83s3qsp5qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsdqqcqzys9qypqsqp2h6a5xeytuc3fad2ed4gxvhd593lwjdna3dxsyeem0qkzjx6guk44jend0xq4zzvp6f3fy07wnmxezazzsxgmvqee8shxjuqu2eu0qpnvc95x", + net=constants.BitcoinSimnet) self.assertEqual(144, lnaddr.get_min_final_cltv_delta()) - lnaddr = lndecode("lntb15u1p0m6lzupp5zqjthgvaad9mewmdjuehwddyze9d8zyxcc43zhaddeegt37sndgsdq4xysyymr0vd4kzcmrd9hx7cqp7xqrrss9qy9qsqsp5vlhcs24hwm747w8f3uau2tlrdkvjaglffnsstwyamj84cxuhrn2s8tut3jqumepu42azyyjpgqa4w9w03204zp9h4clk499y2umstl6s29hqyj8vv4as6zt5567ux7l3f66m8pjhk65zjaq2esezk7ll2kcpljewkg", - net=constants.BitcoinTestnet) + 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)) diff --git a/tests/test_commands.py b/tests/test_commands.py index 69b833b7a..f294c6da5 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -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 diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 6dc07e561..cf06af5df 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -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