Repository navigation
Description
verify_hrp accepts invalid amounts in the human-readable part, so lnbc+25m decodes with no amount, lnbc64=n decodes as 64 BTC, and the official sub-millisatoshi vector decodes with amount_msat 250000000. This happens on main at 51bd3b97, which is the v2.2.0 tag, and in release 2.1.1.
The test at tests/test_hrp_amount.py asserts that verify_hrp rejects the three human-readable parts and that decode rejects the three invoices:
import pytest
from bolt11 import decode
from bolt11.exceptions import Bolt11Exception, Bolt11HrpInvalidException
from bolt11.utils import verify_hrp
# The byte layout of a malformed human-readable part cannot be produced by
# encode, so these inputs stay literals.
# "lnbc+25m": the '+' ends the amount match and the rest of the hrp is ignored.
NO_AMOUNT = (
"lnbc+25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5"
"vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg"
"s9qrsgqdm5y5lfv65tuksj8caqm3f7p6jqdyrj68h8l98yhp7v5ees9yugjaar2eulvh7wq02a"
"cc5mm7kec5nfwj2dqmxsw3ph4mcrw404d42cq8n48kc"
)
# "lnbc64=n": the amount reads as 64 bitcoin and the '=n' is ignored.
SIXTY_FOUR_BTC = (
"lnbc64=n1pllllllpp5yu9lx69d839gzwd4s9k6ykl3h2y62pkh7lt4mlv74c7ehahufylssp5"
"gcrp86sukx8e6rgv5suwq3muss5e5wg66pc78s44tfmamhvtzc5qhp58yjmdan79s6qqdhdzgy"
"nm4zwqd5d7xmw5fk98klysy043l2ahrqsm8xpnjdjtca6mq67huyl5dqv2yltr2efd67x06mjh"
"9zlmfgamyyyy3a26uw7wxe3kqdq32n650muz490glrppsuhjd3guwt8manpqwqqst47cg"
)
# BOLT 11 vector "Invalid sub-millisatoshi precision.", amount 2500000001p.
SUB_MILLISATOSHI = (
"lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqf"
"qypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg"
"3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx"
"3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x"
)
class TestInvalidHrpAmount:
"""
A reader MUST fail an amount that contains a non-digit or is followed by
anything except a multiplier, and a `p` amount whose last digit is not 0.
https://github.com/lightning/bolts/blob/master/11-payment-encoding.md#human-readable-part
"""
@pytest.mark.parametrize(
"hrp",
["lnbc+25m", "lnbc64=n", "lnbc2500000001p"],
ids=["trailing-plus", "trailing-equals", "sub-millisatoshi"],
)
def test_verify_hrp_rejects_invalid_amount(self, hrp):
with pytest.raises(Bolt11HrpInvalidException):
verify_hrp(hrp)
@pytest.mark.parametrize(
"invoice",
[NO_AMOUNT, SIXTY_FOUR_BTC, SUB_MILLISATOSHI],
ids=["no-amount", "sixty-four-btc", "sub-millisatoshi"],
)
def test_decode_rejects_invalid_hrp_amount(self, invoice):
with pytest.raises(Bolt11Exception):
decode(invoice)The command python -m pytest tests/test_hrp_amount.py fails every case:
collected 6 items
tests/test_hrp_amount.py FFFFFF
___ test_verify_hrp_rejects_invalid_amount[trailing-plus] ___
E Failed: DID NOT RAISE Bolt11HrpInvalidException
___ test_verify_hrp_rejects_invalid_amount[trailing-equals] ___
E Failed: DID NOT RAISE Bolt11HrpInvalidException
___ test_verify_hrp_rejects_invalid_amount[sub-millisatoshi] ___
E Failed: DID NOT RAISE Bolt11HrpInvalidException
___ test_decode_rejects_invalid_hrp_amount[no-amount] ___
E Failed: DID NOT RAISE Bolt11Exception
___ test_decode_rejects_invalid_hrp_amount[sixty-four-btc] ___
E Failed: DID NOT RAISE Bolt11Exception
___ test_decode_rejects_invalid_hrp_amount[sub-millisatoshi] ___
E Failed: DID NOT RAISE Bolt11Exception
6 failed
verify_hrp reads the amount with match(r"ln(bcrt|bc|tbs|tb)(\w+)?", hrp) at utils.py#L10. The pattern has no end anchor, so the match stops at the first character that is not a word character and nothing checks the rest of the hrp. amount_to_msat does not check the last digit of a p amount, and MilliSatoshi.from_btc truncates at types.py#L25-L27.
BOLT 11 states the Human-Readable Part reader requirements at L80-L87:
if
amountcontains a non-digit OR is followed by anything except amultiplier(see table above): MUST fail the payment.if multiplier is
pand the last decimal ofamountis not 0: MUST fail the payment.
decode should raise a Bolt11Exception for each of the three inputs.
Activity