lnaddr: make payment_secret field mandatory, in both directions
we now require payment_secret both for sending and for receiving (previously was optional for both) see https://github.com/lightning/bolts/pull/898 https://github.com/ACINQ/eclair/pull/1810 https://github.com/ElementsProject/lightning/pull/4646 note: payment_secret depends on var_onion_optin, so that becomes mandatory as well, however this commit does not yet remove the ability of creating legacy onions
This commit is contained in:
@@ -184,6 +184,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
|
|||||||
tags_set = set()
|
tags_set = set()
|
||||||
|
|
||||||
# Payment hash
|
# Payment hash
|
||||||
|
assert addr.paymenthash is not None
|
||||||
data += tagged_bytes('p', addr.paymenthash)
|
data += tagged_bytes('p', addr.paymenthash)
|
||||||
tags_set.add('p')
|
tags_set.add('p')
|
||||||
|
|
||||||
@@ -196,7 +197,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
|
|||||||
# BOLT #11:
|
# BOLT #11:
|
||||||
#
|
#
|
||||||
# A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields,
|
# A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields,
|
||||||
if k in ('d', 'h', 'n', 'x', 'p', 's'):
|
if k in ('d', 'h', 'n', 'x', 'p', 's', '9'):
|
||||||
if k in tags_set:
|
if k in tags_set:
|
||||||
raise LnEncodeException("Duplicate '{}' tag".format(k))
|
raise LnEncodeException("Duplicate '{}' tag".format(k))
|
||||||
|
|
||||||
@@ -317,6 +318,17 @@ class LnAddr(object):
|
|||||||
from .lnutil import LnFeatures
|
from .lnutil import LnFeatures
|
||||||
return LnFeatures(self.get_tag('9') or 0)
|
return LnFeatures(self.get_tag('9') or 0)
|
||||||
|
|
||||||
|
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,
|
||||||
|
old saved already paid invoices could no longer be parsed.
|
||||||
|
"""
|
||||||
|
from .lnutil import validate_features, ln_compare_features
|
||||||
|
invoice_features = self.get_features()
|
||||||
|
validate_features(invoice_features)
|
||||||
|
ln_compare_features(myfeatures.for_invoice(), invoice_features)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
|
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
|
||||||
hexlify(self.pubkey.serialize()).decode('utf-8') if self.pubkey else None,
|
hexlify(self.pubkey.serialize()).decode('utf-8') if self.pubkey else None,
|
||||||
@@ -381,6 +393,9 @@ class SerializableKey:
|
|||||||
return self.pubkey.get_public_key_bytes(True)
|
return self.pubkey.get_public_key_bytes(True)
|
||||||
|
|
||||||
def lndecode(invoice: str, *, verbose=False, net=None) -> LnAddr:
|
def lndecode(invoice: str, *, verbose=False, net=None) -> LnAddr:
|
||||||
|
"""Parses a string into an LnAddr object.
|
||||||
|
Can raise LnDecodeException or IncompatibleOrInsaneFeatures.
|
||||||
|
"""
|
||||||
if net is None:
|
if net is None:
|
||||||
net = constants.net
|
net = constants.net
|
||||||
decoded_bech32 = bech32_decode(invoice, ignore_long_length=True)
|
decoded_bech32 = bech32_decode(invoice, ignore_long_length=True)
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class OnionHopsDataSingle: # called HopData in lnd
|
|||||||
elif first_byte == b'\x01':
|
elif first_byte == b'\x01':
|
||||||
# reserved for future use
|
# reserved for future use
|
||||||
raise Exception("unsupported hop payload: length==1")
|
raise Exception("unsupported hop payload: length==1")
|
||||||
else:
|
else: # tlv format
|
||||||
hop_payload_length = read_bigsize_int(fd)
|
hop_payload_length = read_bigsize_int(fd)
|
||||||
hop_payload = fd.read(hop_payload_length)
|
hop_payload = fd.read(hop_payload_length)
|
||||||
if hop_payload_length != len(hop_payload):
|
if hop_payload_length != len(hop_payload):
|
||||||
@@ -266,8 +266,9 @@ def calc_hops_data_for_payment(
|
|||||||
route: 'LNPaymentRoute',
|
route: 'LNPaymentRoute',
|
||||||
amount_msat: int,
|
amount_msat: int,
|
||||||
final_cltv: int, *,
|
final_cltv: int, *,
|
||||||
total_msat=None,
|
total_msat: int,
|
||||||
payment_secret: bytes = None) -> Tuple[List[OnionHopsDataSingle], int, int]:
|
payment_secret: bytes,
|
||||||
|
) -> Tuple[List[OnionHopsDataSingle], int, int]:
|
||||||
|
|
||||||
"""Returns the hops_data to be used for constructing an onion packet,
|
"""Returns the hops_data to be used for constructing an onion packet,
|
||||||
and the amount_msat and cltv to be used on our immediate channel.
|
and the amount_msat and cltv to be used on our immediate channel.
|
||||||
@@ -283,12 +284,11 @@ def calc_hops_data_for_payment(
|
|||||||
}
|
}
|
||||||
# for multipart payments we need to tell the receiver about the total and
|
# for multipart payments we need to tell the receiver about the total and
|
||||||
# partial amounts
|
# partial amounts
|
||||||
if payment_secret is not None:
|
hop_payload["payment_data"] = {
|
||||||
hop_payload["payment_data"] = {
|
"payment_secret": payment_secret,
|
||||||
"payment_secret": payment_secret,
|
"total_msat": total_msat,
|
||||||
"total_msat": total_msat,
|
"amount_msat": amt
|
||||||
"amount_msat": amt
|
}
|
||||||
}
|
|
||||||
hops_data = [OnionHopsDataSingle(
|
hops_data = [OnionHopsDataSingle(
|
||||||
is_tlv_payload=route[-1].has_feature_varonion(), payload=hop_payload)]
|
is_tlv_payload=route[-1].has_feature_varonion(), payload=hop_payload)]
|
||||||
# payloads, backwards from last hop (but excluding the first edge):
|
# payloads, backwards from last hop (but excluding the first edge):
|
||||||
|
|||||||
@@ -1441,7 +1441,7 @@ class Peer(Logger):
|
|||||||
total_msat: int,
|
total_msat: int,
|
||||||
payment_hash: bytes,
|
payment_hash: bytes,
|
||||||
min_final_cltv_expiry: int,
|
min_final_cltv_expiry: int,
|
||||||
payment_secret: bytes = None,
|
payment_secret: bytes,
|
||||||
trampoline_onion=None) -> UpdateAddHtlc:
|
trampoline_onion=None) -> UpdateAddHtlc:
|
||||||
|
|
||||||
assert amount_msat > 0, "amount_msat is not greater zero"
|
assert amount_msat > 0, "amount_msat is not greater zero"
|
||||||
@@ -1786,7 +1786,8 @@ class Peer(Logger):
|
|||||||
try:
|
try:
|
||||||
total_msat = processed_onion.hop_data.payload["payment_data"]["total_msat"]
|
total_msat = processed_onion.hop_data.payload["payment_data"]["total_msat"]
|
||||||
except Exception:
|
except Exception:
|
||||||
total_msat = amt_to_forward # fall back to "amt_to_forward"
|
log_fail_reason(f"'total_msat' missing from onion")
|
||||||
|
raise exc_incorrect_or_unknown_pd
|
||||||
|
|
||||||
if not is_trampoline and amt_to_forward != htlc.amount_msat:
|
if not is_trampoline and amt_to_forward != htlc.amount_msat:
|
||||||
log_fail_reason(f"amt_to_forward != htlc.amount_msat")
|
log_fail_reason(f"amt_to_forward != htlc.amount_msat")
|
||||||
@@ -1795,14 +1796,10 @@ class Peer(Logger):
|
|||||||
data=htlc.amount_msat.to_bytes(8, byteorder="big"))
|
data=htlc.amount_msat.to_bytes(8, byteorder="big"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payment_secret_from_onion = processed_onion.hop_data.payload["payment_data"]["payment_secret"]
|
payment_secret_from_onion = processed_onion.hop_data.payload["payment_data"]["payment_secret"] # type: bytes
|
||||||
except Exception:
|
except Exception:
|
||||||
if total_msat > amt_to_forward:
|
log_fail_reason(f"'payment_secret' missing from onion")
|
||||||
# payment_secret is required for MPP
|
raise exc_incorrect_or_unknown_pd
|
||||||
log_fail_reason(f"'payment_secret' missing from onion")
|
|
||||||
raise exc_incorrect_or_unknown_pd
|
|
||||||
# TODO fail here if invoice has set PAYMENT_SECRET_REQ
|
|
||||||
payment_secret_from_onion = None
|
|
||||||
|
|
||||||
payment_status = self.lnworker.check_received_htlc(payment_secret_from_onion, chan.short_channel_id, htlc, total_msat)
|
payment_status = self.lnworker.check_received_htlc(payment_secret_from_onion, chan.short_channel_id, htlc, total_msat)
|
||||||
if payment_status is None:
|
if payment_status is None:
|
||||||
@@ -1825,14 +1822,13 @@ class Peer(Logger):
|
|||||||
log_fail_reason(f"no payment_info found for RHASH {htlc.payment_hash.hex()}")
|
log_fail_reason(f"no payment_info found for RHASH {htlc.payment_hash.hex()}")
|
||||||
raise exc_incorrect_or_unknown_pd
|
raise exc_incorrect_or_unknown_pd
|
||||||
preimage = self.lnworker.get_preimage(htlc.payment_hash)
|
preimage = self.lnworker.get_preimage(htlc.payment_hash)
|
||||||
if payment_secret_from_onion:
|
expected_payment_secrets = [self.lnworker.get_payment_secret(htlc.payment_hash)]
|
||||||
expected_payment_secrets = [self.lnworker.get_payment_secret(htlc.payment_hash)]
|
if preimage:
|
||||||
if preimage:
|
# legacy secret for old invoices
|
||||||
# legacy secret for old invoices
|
expected_payment_secrets.append(derive_payment_secret_from_payment_preimage(preimage))
|
||||||
expected_payment_secrets.append(derive_payment_secret_from_payment_preimage(preimage))
|
if payment_secret_from_onion not in expected_payment_secrets:
|
||||||
if payment_secret_from_onion not in expected_payment_secrets:
|
log_fail_reason(f'incorrect payment secret {payment_secret_from_onion.hex()} != {expected_payment_secrets[0].hex()}')
|
||||||
log_fail_reason(f'incorrect payment secret {payment_secret_from_onion.hex()} != {expected_payment_secrets[0].hex()}')
|
raise exc_incorrect_or_unknown_pd
|
||||||
raise exc_incorrect_or_unknown_pd
|
|
||||||
invoice_msat = info.amount_msat
|
invoice_msat = info.amount_msat
|
||||||
if not (invoice_msat is None or invoice_msat <= total_msat <= 2 * invoice_msat):
|
if not (invoice_msat is None or invoice_msat <= total_msat <= 2 * invoice_msat):
|
||||||
log_fail_reason(f"total_msat={total_msat} too different from invoice_msat={invoice_msat}")
|
log_fail_reason(f"total_msat={total_msat} too different from invoice_msat={invoice_msat}")
|
||||||
|
|||||||
@@ -194,6 +194,8 @@ LNWALLET_FEATURES = (
|
|||||||
| LnFeatures.OPTION_DATA_LOSS_PROTECT_REQ
|
| LnFeatures.OPTION_DATA_LOSS_PROTECT_REQ
|
||||||
| LnFeatures.OPTION_STATIC_REMOTEKEY_REQ
|
| LnFeatures.OPTION_STATIC_REMOTEKEY_REQ
|
||||||
| LnFeatures.GOSSIP_QUERIES_REQ
|
| LnFeatures.GOSSIP_QUERIES_REQ
|
||||||
|
| LnFeatures.VAR_ONION_REQ
|
||||||
|
| LnFeatures.PAYMENT_SECRET_REQ
|
||||||
| LnFeatures.BASIC_MPP_OPT
|
| LnFeatures.BASIC_MPP_OPT
|
||||||
| LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM
|
| LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM
|
||||||
| LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_OPT
|
| LnFeatures.OPTION_SHUTDOWN_ANYSEGWIT_OPT
|
||||||
@@ -1238,7 +1240,7 @@ class LNWallet(LNWorker):
|
|||||||
self, *,
|
self, *,
|
||||||
node_pubkey: bytes,
|
node_pubkey: bytes,
|
||||||
payment_hash: bytes,
|
payment_hash: bytes,
|
||||||
payment_secret: Optional[bytes],
|
payment_secret: bytes,
|
||||||
amount_to_pay: int, # in msat
|
amount_to_pay: int, # in msat
|
||||||
min_cltv_expiry: int,
|
min_cltv_expiry: int,
|
||||||
r_tags,
|
r_tags,
|
||||||
@@ -1389,7 +1391,7 @@ class LNWallet(LNWorker):
|
|||||||
total_msat: int,
|
total_msat: int,
|
||||||
amount_receiver_msat:int,
|
amount_receiver_msat:int,
|
||||||
payment_hash: bytes,
|
payment_hash: bytes,
|
||||||
payment_secret: Optional[bytes],
|
payment_secret: bytes,
|
||||||
min_cltv_expiry: int,
|
min_cltv_expiry: int,
|
||||||
trampoline_onion: bytes = None,
|
trampoline_onion: bytes = None,
|
||||||
trampoline_fee_level: int,
|
trampoline_fee_level: int,
|
||||||
@@ -1546,8 +1548,7 @@ class LNWallet(LNWorker):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
def _check_invoice(self, invoice: str, *, amount_msat: int = None) -> LnAddr:
|
||||||
def _check_invoice(invoice: str, *, amount_msat: int = None) -> LnAddr:
|
|
||||||
addr = lndecode(invoice)
|
addr = lndecode(invoice)
|
||||||
if addr.is_expired():
|
if addr.is_expired():
|
||||||
raise InvoiceError(_("This invoice has expired"))
|
raise InvoiceError(_("This invoice has expired"))
|
||||||
@@ -1562,6 +1563,7 @@ class LNWallet(LNWorker):
|
|||||||
raise InvoiceError("{}\n{}".format(
|
raise InvoiceError("{}\n{}".format(
|
||||||
_("Invoice wants us to risk locking funds for unreasonably long."),
|
_("Invoice wants us to risk locking funds for unreasonably long."),
|
||||||
f"min_final_cltv_expiry: {addr.get_min_final_cltv_expiry()}"))
|
f"min_final_cltv_expiry: {addr.get_min_final_cltv_expiry()}"))
|
||||||
|
addr.validate_and_compare_features(self.features)
|
||||||
return addr
|
return addr
|
||||||
|
|
||||||
def is_trampoline_peer(self, node_id: bytes) -> bool:
|
def is_trampoline_peer(self, node_id: bytes) -> bool:
|
||||||
@@ -1615,8 +1617,8 @@ class LNWallet(LNWorker):
|
|||||||
min_cltv_expiry,
|
min_cltv_expiry,
|
||||||
r_tags,
|
r_tags,
|
||||||
invoice_features: int,
|
invoice_features: int,
|
||||||
payment_hash,
|
payment_hash: bytes,
|
||||||
payment_secret,
|
payment_secret: bytes,
|
||||||
trampoline_fee_level: int,
|
trampoline_fee_level: int,
|
||||||
use_two_trampolines: bool,
|
use_two_trampolines: bool,
|
||||||
fwd_trampoline_onion=None,
|
fwd_trampoline_onion=None,
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import unittest
|
|||||||
from electrum.lnaddr import shorten_amount, unshorten_amount, LnAddr, lnencode, lndecode, u5_to_bitarray, bitarray_to_u5
|
from electrum.lnaddr import shorten_amount, unshorten_amount, LnAddr, lnencode, lndecode, u5_to_bitarray, bitarray_to_u5
|
||||||
from electrum.segwit_addr import bech32_encode, bech32_decode
|
from electrum.segwit_addr import bech32_encode, bech32_decode
|
||||||
from electrum import segwit_addr
|
from electrum import segwit_addr
|
||||||
from electrum.lnutil import UnknownEvenFeatureBits, derive_payment_secret_from_payment_preimage, LnFeatures
|
from electrum.lnutil import UnknownEvenFeatureBits, derive_payment_secret_from_payment_preimage, LnFeatures, IncompatibleLightningFeatures
|
||||||
from electrum import constants
|
from electrum import constants
|
||||||
|
|
||||||
from . import ElectrumTestCase
|
from . import ElectrumTestCase
|
||||||
|
|
||||||
|
|
||||||
RHASH=unhexlify('0001020304050607080900010203040506070809000102030405060708090102')
|
RHASH=unhexlify('0001020304050607080900010203040506070809000102030405060708090102')
|
||||||
|
PAYMENT_SECRET=unhexlify('1111111111111111111111111111111111111111111111111111111111111111')
|
||||||
CONVERSION_RATE=1200
|
CONVERSION_RATE=1200
|
||||||
PRIVKEY=unhexlify('e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734')
|
PRIVKEY=unhexlify('e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734')
|
||||||
PUBKEY=unhexlify('03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad')
|
PUBKEY=unhexlify('03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad')
|
||||||
@@ -65,6 +66,41 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
|
|
||||||
timestamp = 1615922274
|
timestamp = 1615922274
|
||||||
tests = [
|
tests = [
|
||||||
|
(LnAddr(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)]),
|
||||||
|
"lnbc1m1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5xysxxatsyp3k7enxv4jsxqzpu9qy9qsqw8l2pulslacwjt86vle3sgfdmcct5v34gtcpfnujsf6ufqa7v7jzdpddnwgte82wkscdlwfwucrgn8z36rv9hzk5mukltteh0yqephqpk5vegu"),
|
||||||
|
(LnAddr(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)]),
|
||||||
|
"lntb1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfpp3x9et2e20v6pu37c5d9vax37wxq72un98hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqy5826t0z3sn29z396pmr4kv73lcx0v7y6vas6h3pysmqllmzwgm5ps2t468gm4psj52usjy6y4xcry4k84n2zggs6f9agwg95454v6gqrwmh4f"),
|
||||||
|
(LnAddr(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)]),
|
||||||
|
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfppj3a24vwu6r8ejrss3axul8rxldph2q7z9hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqqf6z4r7ruzr5txm5ln4netwa2f4x233tud7jy8gxrynyx07rxt7qm92yk2krlgwr7d8jknglur75sujeyapmda5nf3femrk2mep8a2cp4hlvup"),
|
||||||
|
(LnAddr(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)]),
|
||||||
|
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qy9qsqgt4gg9uktlpgnnuvczazusp5uwjv78na305ucsw06c8uk58e5stjqj9sz7fgavw0z688alt364js72mc9mg8yumhpes2dsmq5k9nr5qqddykxy"),
|
||||||
|
(LnAddr(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))]),
|
||||||
|
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqszrwfgrl5k3rt4q4mclc8t00p2tcjsf9pmpcq6lu5zhmampyvk43fk30eqpdm8t5qmdpzan25aqxqaqdzmy0smrtduazjcxx975vz78ccpx0qhev"),
|
||||||
|
(LnAddr(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))]),
|
||||||
|
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqs2dr525u5f4kjxdv0hq5c822qwxrtttjl4u586yl84x0kvvx66gz9ygy76005s5sjwgr7fp55ccsae47vpl4gqvwhc3exps964g743j5gqwtt68t"),
|
||||||
|
(LnAddr(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', '')]),
|
(LnAddr(date=timestamp, paymenthash=RHASH, tags=[('d', '')]),
|
||||||
"lnbc1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqd9n3kwjjwglnfne5p4rvkze998m3xcxrc8kunl5khkchlaqhwhlyztuuwkrglv47mqg96mcqjjx70hh9luaj4te0u4ww6aclxwve3fqpkmdxlj"),
|
"lnbc1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqd9n3kwjjwglnfne5p4rvkze998m3xcxrc8kunl5khkchlaqhwhlyztuuwkrglv47mqg96mcqjjx70hh9luaj4te0u4ww6aclxwve3fqpkmdxlj"),
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60)]),
|
(LnAddr(date=timestamp, paymenthash=RHASH, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60)]),
|
||||||
@@ -73,33 +109,8 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
"lnbc11ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs2qjafckq94q3js6lvqz2kmenn9ysjejyj8fm4hlx0xtqhaxfzlxjappkgp0hmm40dnuan4v3jy83lqjup2n0fdzgysg049y9l9uc98qq07kfd3"),
|
"lnbc11ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs2qjafckq94q3js6lvqz2kmenn9ysjejyj8fm4hlx0xtqhaxfzlxjappkgp0hmm40dnuan4v3jy83lqjup2n0fdzgysg049y9l9uc98qq07kfd3"),
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, net=constants.BitcoinTestnet, tags=[('f', 'mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'), ('h', longdescription)]),
|
(LnAddr(date=timestamp, paymenthash=RHASH, net=constants.BitcoinTestnet, tags=[('f', 'mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'), ('h', longdescription)]),
|
||||||
"lntb1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsr9zktgu78k8p9t8555ve37qwfvqn6ga37fnfwhgexmf20nzdpmuhwvuv7zra3xrh8y2ggxxuemqfsgka9x7uzsrcx8rfv85c8pmhq9gq4sampn"),
|
"lntb1ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsr9zktgu78k8p9t8555ve37qwfvqn6ga37fnfwhgexmf20nzdpmuhwvuv7zra3xrh8y2ggxxuemqfsgka9x7uzsrcx8rfv85c8pmhq9gq4sampn"),
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[
|
|
||||||
('r', [(unhexlify('029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'), unhexlify('0102030405060708'), 1, 20, 3),
|
])
|
||||||
(unhexlify('039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'), unhexlify('030405060708090a'), 2, 30, 4)]),
|
|
||||||
('f', '1RustyRX2oai4EYYDpQGWvEL62BBGqN9T'),
|
|
||||||
('h', longdescription)]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsq68hmxx9ar8eh9nq6gcafxd4vn4mqy458f744t0lms3anm2svydxx2lv84ardcks83u0h34u3lvflh0x9y8qdgjj3q3lxqp5kzqueygqema2z9"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('f', '3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX'), ('h', longdescription)]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z9hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfa9a608cewefn0n6wflmd27s4nvevru262k2uj34wq58c4y5tqjrs77kvd5umnjgpndxfchde0h0mc07l65agyh9dqlgz5ujhpe8ewspsve8hh"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('f', 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'), ('h', longdescription)]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7khp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqstrtguf9h6ur3n3dchft84q46yy50gf0vugq8g3n88txqcn25dhg98tt4wvlhy967cdarj6cznwn3uyssqeu0e3jgdt9mh5nz9xyqsggpnp2hht"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('f', 'bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3'), ('h', longdescription)]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsvv679nlk4m93cahuxv04qqv6q8gshqu5f5tcgcasayuejxny4t4rpugqh4fy4zrma23ts93zclhsm694pu9ll0qlfaqkpstu7u02l8gq6fr4jy"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('n', PUBKEY), ('h', longdescription)]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66hp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqst7hmgl7lmqxaael9g7w3e43acceyz93920457yv2egsfkcpnxqf9p0wu8x6dy34k580rulrtvt77f757g2k9lkf7ggph4pyux6e8wksq5ejkr3"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('h', longdescription), ('9', 514)]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qzsz20x48k6dgxsrrsqhccvuwtsjny2flcyhlpyuz5lufn4wvjml7wwkaaxfyxpkk2j84hq4xdvm2pt265hm7jy97p5f34gu2tcwgvd9j4gqcam6kj"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 8))]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qzg2f9ep5rqksjjdjzq20eqkwvsd0gx0llf2lv6x395l3ph82naeqkg3slj7s326sqnk4ql32acs2fft4p5tyjt8ujxtnhauu4mp7w4xgaqpp7a6ha"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9))]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qzs2035s0h84dfv9lykfcscuh5phy8mmq53nyu9szwln7d02xaz57t59p22pkzavenfa8qetvtkf27l9h9n3k55puvx6573d7fwhmwp6cvcqjvjqe7"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9) + (1 << 14))]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrss2y8hzphx329clpfz86r60zd3ctn2q0uuakge6qws075r7sf43r8wpmrv36ujj68mzdw6rhkxy4mal5zullec8v6yjnnsh093qjwc5cuspz34uag"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('h', longdescription), ('9', 10 + (1 << 9) + (1 << 15))]),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqs2gc0fc84x29vk0pmq6p4qcn2ttn9azxtfrf2xqz00e79cfvf4nqvx96hz94uqsh4j4hnyywp63nagddwm0zdscprvkqlhltysa478x3sqkee5v9"),
|
|
||||||
(LnAddr(date=timestamp, paymenthash=RHASH, amount=24, tags=[('h', longdescription), ('9', 33282)], payment_secret=b"\x11" * 32),
|
|
||||||
"lnbc241ps9zprzpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qypqszrwfgrl5k3rt4q4mclc8t00p2tcjsf9pmpcq6lu5zhmampyvk43fk30eqpdm8t5qmdpzan25aqxqaqdzmy0smrtduazjcxx975vz78ccpx0qhev"),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Roundtrip
|
# Roundtrip
|
||||||
for lnaddr1, invoice_str1 in tests:
|
for lnaddr1, invoice_str1 in tests:
|
||||||
@@ -112,7 +123,7 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
# We flip the signature recovery bit, which would normally give a different
|
# We flip the signature recovery bit, which would normally give a different
|
||||||
# pubkey.
|
# pubkey.
|
||||||
_, hrp, data = bech32_decode(
|
_, hrp, data = bech32_decode(
|
||||||
lnencode(LnAddr(paymenthash=RHASH, amount=24, tags=[('d', '')]), PRIVKEY),
|
lnencode(LnAddr(paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('d', ''), ('9', 33282)]), PRIVKEY),
|
||||||
ignore_long_length=True)
|
ignore_long_length=True)
|
||||||
databits = u5_to_bitarray(data)
|
databits = u5_to_bitarray(data)
|
||||||
databits.invert(-1)
|
databits.invert(-1)
|
||||||
@@ -121,7 +132,7 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
|
|
||||||
# But not if we supply expliciy `n` specifier!
|
# But not if we supply expliciy `n` specifier!
|
||||||
_, hrp, data = bech32_decode(
|
_, hrp, data = bech32_decode(
|
||||||
lnencode(LnAddr(paymenthash=RHASH, amount=24, tags=[('d', ''), ('n', PUBKEY)]), PRIVKEY),
|
lnencode(LnAddr(paymenthash=RHASH, payment_secret=PAYMENT_SECRET, amount=24, tags=[('d', ''), ('n', PUBKEY), ('9', 33282)]), PRIVKEY),
|
||||||
ignore_long_length=True)
|
ignore_long_length=True)
|
||||||
databits = u5_to_bitarray(data)
|
databits = u5_to_bitarray(data)
|
||||||
databits.invert(-1)
|
databits.invert(-1)
|
||||||
@@ -129,7 +140,7 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
assert lnaddr.pubkey.serialize() == PUBKEY
|
assert lnaddr.pubkey.serialize() == PUBKEY
|
||||||
|
|
||||||
def test_min_final_cltv_expiry_decoding(self):
|
def test_min_final_cltv_expiry_decoding(self):
|
||||||
lnaddr = lndecode("lnsb500u1pdsgyf3pp5nmrqejdsdgs4n9ukgxcp2kcq265yhrxd4k5dyue58rxtp5y83s3qdqqcqzystrggccm9yvkr5yqx83jxll0qjpmgfg9ywmcd8g33msfgmqgyfyvqhku80qmqm8q6v35zvck2y5ccxsz5avtrauz8hgjj3uahppyq20qp6dvwxe",
|
lnaddr = lndecode("lnsb500u1pdsgyf3pp5nmrqejdsdgs4n9ukgxcp2kcq265yhrxd4k5dyue58rxtp5y83s3qsp5qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsdqqcqzys9qypqsqp2h6a5xeytuc3fad2ed4gxvhd593lwjdna3dxsyeem0qkzjx6guk44jend0xq4zzvp6f3fy07wnmxezazzsxgmvqee8shxjuqu2eu0qpnvc95x",
|
||||||
net=constants.BitcoinSimnet)
|
net=constants.BitcoinSimnet)
|
||||||
self.assertEqual(144, lnaddr.get_min_final_cltv_expiry())
|
self.assertEqual(144, lnaddr.get_min_final_cltv_expiry())
|
||||||
|
|
||||||
@@ -139,15 +150,16 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
|
|
||||||
def test_min_final_cltv_expiry_roundtrip(self):
|
def test_min_final_cltv_expiry_roundtrip(self):
|
||||||
for cltv in (1, 15, 16, 31, 32, 33, 150, 511, 512, 513, 1023, 1024, 1025):
|
for cltv in (1, 15, 16, 31, 32, 33, 150, 511, 512, 513, 1023, 1024, 1025):
|
||||||
lnaddr = LnAddr(paymenthash=RHASH, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60), ('c', cltv)])
|
lnaddr = LnAddr(
|
||||||
|
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_expiry())
|
self.assertEqual(cltv, lnaddr.get_min_final_cltv_expiry())
|
||||||
invoice = lnencode(lnaddr, PRIVKEY)
|
invoice = lnencode(lnaddr, PRIVKEY)
|
||||||
self.assertEqual(cltv, lndecode(invoice).get_min_final_cltv_expiry())
|
self.assertEqual(cltv, lndecode(invoice).get_min_final_cltv_expiry())
|
||||||
|
|
||||||
def test_features(self):
|
def test_features(self):
|
||||||
lnaddr = lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9qzsze992adudgku8p05pstl6zh7av6rx2f297pv89gu5q93a0hf3g7lynl3xq56t23dpvah6u7y9qey9lccrdml3gaqwc6nxsl5ktzm464sq73t7cl")
|
lnaddr = lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9qypqsztrz5v3jfnxskfv7g8chmyzyrfhf2vupcavuq5rce96kyt6g0zh337h206awccwp335zarqrud4wccgdn39vur44d8um4hmgv06aj0sgpdrv73z")
|
||||||
self.assertEqual(514, lnaddr.get_tag('9'))
|
self.assertEqual(33282, lnaddr.get_tag('9'))
|
||||||
self.assertEqual(LnFeatures(514), lnaddr.get_features())
|
self.assertEqual(LnFeatures(33282), lnaddr.get_features())
|
||||||
|
|
||||||
with self.assertRaises(UnknownEvenFeatureBits):
|
with self.assertRaises(UnknownEvenFeatureBits):
|
||||||
lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9q4pqqqqqqqqqqqqqqqqqqszk3ed62snp73037h4py4gry05eltlp0uezm2w9ajnerhmxzhzhsu40g9mgyx5v3ad4aqwkmvyftzk4k9zenz90mhjcy9hcevc7r3lx2sphzfxz7")
|
lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9q4pqqqqqqqqqqqqqqqqqqszk3ed62snp73037h4py4gry05eltlp0uezm2w9ajnerhmxzhzhsu40g9mgyx5v3ad4aqwkmvyftzk4k9zenz90mhjcy9hcevc7r3lx2sphzfxz7")
|
||||||
@@ -161,3 +173,9 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
preimage = bytes.fromhex("cc3fc000bdeff545acee53ada12ff96060834be263f77d645abbebc3a8d53b92")
|
preimage = bytes.fromhex("cc3fc000bdeff545acee53ada12ff96060834be263f77d645abbebc3a8d53b92")
|
||||||
self.assertEqual("bfd660b559b3f452c6bb05b8d2906f520c151c107b733863ed0cc53fc77021a8",
|
self.assertEqual("bfd660b559b3f452c6bb05b8d2906f520c151c107b733863ed0cc53fc77021a8",
|
||||||
derive_payment_secret_from_payment_preimage(preimage).hex())
|
derive_payment_secret_from_payment_preimage(preimage).hex())
|
||||||
|
|
||||||
|
def test_validate_and_compare_features(self):
|
||||||
|
lnaddr = lndecode("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)))
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
|
|||||||
get_preimage = LNWallet.get_preimage
|
get_preimage = LNWallet.get_preimage
|
||||||
create_route_for_payment = LNWallet.create_route_for_payment
|
create_route_for_payment = LNWallet.create_route_for_payment
|
||||||
create_routes_for_payment = LNWallet.create_routes_for_payment
|
create_routes_for_payment = LNWallet.create_routes_for_payment
|
||||||
_check_invoice = staticmethod(LNWallet._check_invoice)
|
_check_invoice = LNWallet._check_invoice
|
||||||
pay_to_route = LNWallet.pay_to_route
|
pay_to_route = LNWallet.pay_to_route
|
||||||
pay_to_node = LNWallet.pay_to_node
|
pay_to_node = LNWallet.pay_to_node
|
||||||
pay_invoice = LNWallet.pay_invoice
|
pay_invoice = LNWallet.pay_invoice
|
||||||
|
|||||||
@@ -232,7 +232,15 @@ def create_trampoline_route(
|
|||||||
return route
|
return route
|
||||||
|
|
||||||
|
|
||||||
def create_trampoline_onion(*, route, amount_msat, final_cltv, total_msat, payment_hash, payment_secret):
|
def create_trampoline_onion(
|
||||||
|
*,
|
||||||
|
route,
|
||||||
|
amount_msat,
|
||||||
|
final_cltv,
|
||||||
|
total_msat: int,
|
||||||
|
payment_hash: bytes,
|
||||||
|
payment_secret: bytes,
|
||||||
|
):
|
||||||
# all edges are trampoline
|
# all edges are trampoline
|
||||||
hops_data, amount_msat, cltv = calc_hops_data_for_payment(
|
hops_data, amount_msat, cltv = calc_hops_data_for_payment(
|
||||||
route,
|
route,
|
||||||
@@ -281,8 +289,8 @@ def create_trampoline_route_and_onion(
|
|||||||
my_pubkey: bytes,
|
my_pubkey: bytes,
|
||||||
node_id,
|
node_id,
|
||||||
r_tags,
|
r_tags,
|
||||||
payment_hash,
|
payment_hash: bytes,
|
||||||
payment_secret,
|
payment_secret: bytes,
|
||||||
local_height: int,
|
local_height: int,
|
||||||
trampoline_fee_level: int,
|
trampoline_fee_level: int,
|
||||||
use_two_trampolines: bool,
|
use_two_trampolines: bool,
|
||||||
|
|||||||
Reference in New Issue
Block a user