Skip to content

Commit e469492

Browse files
committed
BOLT 12 test vectors for offer parsing
One discrepancy from the spec still needs to be resolved: https://github.com/lightning/bolts/pull/798/files#r1334851959
1 parent 452a30e commit e469492

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed

lightning/src/offers/offer.rs

+217
Original file line numberDiff line numberDiff line change
@@ -1509,3 +1509,220 @@ mod tests {
15091509
}
15101510
}
15111511
}
1512+
1513+
#[cfg(test)]
1514+
mod bolt12_tests {
1515+
use super::{Bolt12ParseError, Bolt12SemanticError, Offer};
1516+
use crate::ln::msgs::DecodeError;
1517+
1518+
#[test]
1519+
fn parses_bech32_encoded_offers() {
1520+
let offers = [
1521+
// Minimal bolt12 offer
1522+
"lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg",
1523+
1524+
// for testnet
1525+
"lno1qgsyxjtl6luzd9t3pr62xr7eemp6awnejusgf6gw45q75vcfqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
1526+
1527+
// for bitcoin (redundant)
1528+
"lno1qgsxlc5vp2m0rvmjcxn2y34wv0m5lyc7sdj7zksgn35dvxgqqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
1529+
1530+
// for bitcoin or liquidv1
1531+
"lno1qfqpge38tqmzyrdjj3x2qkdr5y80dlfw56ztq6yd9sme995g3gsxqqm0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq9qc4r9wd6zqan9vd6x7unnzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
1532+
1533+
// with metadata
1534+
"lno1qsgqqqqqqqqqqqqqqqqqqqqqqqqqqzsv23jhxapqwejkxar0wfe3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
1535+
1536+
// with amount
1537+
"lno1pqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
1538+
1539+
// with currency
1540+
"lno1qcp4256ypqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
1541+
1542+
// with expiry
1543+
"lno1pgx9getnwss8vetrw3hhyucwq3ay997czcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
1544+
1545+
// with issuer
1546+
"lno1pgx9getnwss8vetrw3hhyucjy358garswvaz7tmzdak8gvfj9ehhyeeqgf85c4p3xgsxjmnyw4ehgunfv4e3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
1547+
1548+
// with quantity
1549+
"lno1pgx9getnwss8vetrw3hhyuc5qyz3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
1550+
1551+
// with unlimited (or unknown) quantity
1552+
"lno1pgx9getnwss8vetrw3hhyuc5qqtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry",
1553+
1554+
// with single quantity (weird but valid)
1555+
"lno1pgx9getnwss8vetrw3hhyuc5qyq3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
1556+
1557+
// with feature
1558+
"lno1pgx9getnwss8vetrw3hhyucvp5yqqqqqqqqqqqqqqqqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg",
1559+
1560+
// with blinded path via Bob (0x424242...), blinding 020202...
1561+
"lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zyg3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
1562+
1563+
// ... and with second blinded path via Carol (0x434343...), blinding 020202...
1564+
"lno1pgx9getnwss8vetrw3hhyucsl5q5yqeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzqqqqqqqqqqqqqqqqqqqqqqqqqqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqyzyg3zyg3zyg3zzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
1565+
1566+
// unknown odd field
1567+
"lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxfppf5x2mrvdamk7unvvs",
1568+
];
1569+
for encoded_offer in &offers {
1570+
if let Err(e) = encoded_offer.parse::<Offer>() {
1571+
panic!("Invalid offer ({:?}): {}", e, encoded_offer);
1572+
}
1573+
}
1574+
}
1575+
1576+
#[test]
1577+
fn fails_parsing_bech32_encoded_offers() {
1578+
// Malformed: fields out of order
1579+
assert_eq!(
1580+
"lno1zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszpgz5znzfgdzs".parse::<Offer>(),
1581+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1582+
);
1583+
1584+
// Malformed: unknown even TLV type 78
1585+
assert_eq!(
1586+
"lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpysgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
1587+
Err(Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1588+
);
1589+
1590+
// Malformed: empty
1591+
assert_eq!(
1592+
"lno1".parse::<Offer>(),
1593+
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
1594+
);
1595+
1596+
// Malformed: truncated at type
1597+
assert_eq!(
1598+
"lno1pg".parse::<Offer>(),
1599+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1600+
);
1601+
1602+
// Malformed: truncated in length
1603+
assert_eq!(
1604+
"lno1pt7s".parse::<Offer>(),
1605+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1606+
);
1607+
1608+
// Malformed: truncated after length
1609+
assert_eq!(
1610+
"lno1pgpq".parse::<Offer>(),
1611+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1612+
);
1613+
1614+
// Malformed: truncated in description
1615+
assert_eq!(
1616+
"lno1pgpyz".parse::<Offer>(),
1617+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1618+
);
1619+
1620+
// Malformed: invalid offer_chains length
1621+
assert_eq!(
1622+
"lno1qgqszzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1623+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1624+
);
1625+
1626+
// Malformed: truncated currency UTF-8
1627+
assert_eq!(
1628+
"lno1qcqcqzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1629+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1630+
);
1631+
1632+
// Malformed: invalid currency UTF-8
1633+
assert_eq!(
1634+
"lno1qcpgqsg2q4q5cj2rg5tzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::<Offer>(),
1635+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1636+
);
1637+
1638+
// Malformed: truncated description UTF-8
1639+
assert_eq!(
1640+
"lno1pgqcq93pqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqy".parse::<Offer>(),
1641+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1642+
);
1643+
1644+
// Malformed: invalid description UTF-8
1645+
assert_eq!(
1646+
"lno1pgpgqsgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::<Offer>(),
1647+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1648+
);
1649+
1650+
// Malformed: truncated offer_paths
1651+
assert_eq!(
1652+
"lno1pgz5znzfgdz3qqgpzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1653+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1654+
);
1655+
1656+
// Malformed: zero num_hops in blinded_path
1657+
assert_eq!(
1658+
"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1659+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1660+
);
1661+
1662+
// Malformed: truncated onionmsg_hop in blinded_path
1663+
assert_eq!(
1664+
"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::<Offer>(),
1665+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1666+
);
1667+
1668+
// Malformed: bad first_node_id in blinded_path
1669+
assert_eq!(
1670+
"lno1pgz5znzfgdz3qqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1671+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1672+
);
1673+
1674+
// Malformed: bad blinding in blinded_path
1675+
assert_eq!(
1676+
"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcpqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1677+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1678+
);
1679+
1680+
// Malformed: bad blinded_node_id in onionmsg_hop
1681+
assert_eq!(
1682+
"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1683+
Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
1684+
);
1685+
1686+
// Malformed: truncated issuer UTF-8
1687+
assert_eq!(
1688+
"lno1pgz5znzfgdz3yqvqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
1689+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1690+
);
1691+
1692+
// Malformed: invalid issuer UTF-8
1693+
assert_eq!(
1694+
"lno1pgz5znzfgdz3yq5qgytzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::<Offer>(),
1695+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1696+
);
1697+
1698+
// Malformed: invalid offer_node_id
1699+
assert_eq!(
1700+
"lno1pgz5znzfgdz3vggzqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvps".parse::<Offer>(),
1701+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1702+
);
1703+
1704+
// Contains type >= 80
1705+
assert_eq!(
1706+
"lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
1707+
Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1708+
);
1709+
1710+
// TODO: Resolved in spec https://github.com/lightning/bolts/pull/798/files#r1334851959
1711+
// Contains unknown feature 22
1712+
assert!(
1713+
"lno1pgx9getnwss8vetrw3hhyucvqdqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::<Offer>().is_ok()
1714+
);
1715+
1716+
// Missing offer_description
1717+
assert_eq!(
1718+
"lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese".parse::<Offer>(),
1719+
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
1720+
);
1721+
1722+
// Missing offer_node_id"
1723+
assert_eq!(
1724+
"lno1pgx9getnwss8vetrw3hhyuc".parse::<Offer>(),
1725+
Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)),
1726+
);
1727+
}
1728+
}

0 commit comments

Comments
 (0)