X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Foffer.rs;h=802813fd180f70d9f6006cdc126a69f5c5f8f6e3;hb=7c9463668a4f71663746e57bdb09ee6b91797d5a;hp=66dca85cd94c97d6f5b6c594c24d07114d859860;hpb=5ddd8a7559b1e5d2cee85e5594dddfa5a5c085e9;p=rust-lightning diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index 66dca85c..802813fd 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -339,6 +339,11 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> { self } + pub(crate) fn clear_paths(mut self) -> Self { + self.offer.paths = None; + self + } + pub(super) fn build_unchecked(self) -> Offer { self.build_without_checks() } @@ -1519,28 +1524,60 @@ mod tests { } #[cfg(test)] -mod bech32_tests { - use super::{Bolt12ParseError, Offer}; - use bitcoin::bech32; +mod bolt12_tests { + use super::{Bolt12ParseError, Bolt12SemanticError, Offer}; use crate::ln::msgs::DecodeError; - #[test] - fn encodes_offer_as_bech32_without_checksum() { - let encoded_offer = "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg"; - let offer = dbg!(encoded_offer.parse::().unwrap()); - let reencoded_offer = offer.to_string(); - dbg!(reencoded_offer.parse::().unwrap()); - assert_eq!(reencoded_offer, encoded_offer); - } - #[test] fn parses_bech32_encoded_offers() { let offers = [ - // BOLT 12 test vectors - "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", - "l+no1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", - "lno1pqps7sjqpgt+yzm3qv4uxzmtsd3jjqer9wd3hy6tsw3+5k7msjzfpy7nz5yqcn+ygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd+5xvxg", - "lno1pqps7sjqpgt+ yzm3qv4uxzmtsd3jjqer9wd3hy6tsw3+ 5k7msjzfpy7nz5yqcn+\nygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd+\r\n 5xvxg", + // Minimal bolt12 offer + "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", + + // for testnet + "lno1qgsyxjtl6luzd9t3pr62xr7eemp6awnejusgf6gw45q75vcfqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj", + + // for bitcoin (redundant) + "lno1qgsxlc5vp2m0rvmjcxn2y34wv0m5lyc7sdj7zksgn35dvxgqqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj", + + // for bitcoin or liquidv1 + "lno1qfqpge38tqmzyrdjj3x2qkdr5y80dlfw56ztq6yd9sme995g3gsxqqm0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq9qc4r9wd6zqan9vd6x7unnzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese", + + // with metadata + "lno1qsgqqqqqqqqqqqqqqqqqqqqqqqqqqzsv23jhxapqwejkxar0wfe3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs", + + // with amount + "lno1pqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj", + + // with currency + "lno1qcp4256ypqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj", + + // with expiry + "lno1pgx9getnwss8vetrw3hhyucwq3ay997czcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese", + + // with issuer + "lno1pgx9getnwss8vetrw3hhyucjy358garswvaz7tmzdak8gvfj9ehhyeeqgf85c4p3xgsxjmnyw4ehgunfv4e3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs", + + // with quantity + "lno1pgx9getnwss8vetrw3hhyuc5qyz3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs", + + // with unlimited (or unknown) quantity + "lno1pgx9getnwss8vetrw3hhyuc5qqtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry", + + // with single quantity (weird but valid) + "lno1pgx9getnwss8vetrw3hhyuc5qyq3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs", + + // with feature + "lno1pgx9getnwss8vetrw3hhyucvp5yqqqqqqqqqqqqqqqqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", + + // with blinded path via Bob (0x424242...), blinding 020202... + "lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zyg3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs", + + // ... and with second blinded path via Carol (0x434343...), blinding 020202... + "lno1pgx9getnwss8vetrw3hhyucsl5q5yqeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzqqqqqqqqqqqqqqqqqqqqqqqqqqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqyzyg3zyg3zyg3zzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese", + + // unknown odd field + "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxfppf5x2mrvdamk7unvvs", ]; for encoded_offer in &offers { if let Err(e) = encoded_offer.parse::() { @@ -1550,48 +1587,155 @@ mod bech32_tests { } #[test] - fn fails_parsing_bech32_encoded_offers_with_invalid_continuations() { - let offers = [ - // BOLT 12 test vectors - "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg+", - "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg+ ", - "+lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", - "+ lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", - "ln++o1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", - ]; - for encoded_offer in &offers { - match encoded_offer.parse::() { - Ok(_) => panic!("Valid offer: {}", encoded_offer), - Err(e) => assert_eq!(e, Bolt12ParseError::InvalidContinuation), - } - } + fn fails_parsing_bech32_encoded_offers() { + // Malformed: fields out of order + assert_eq!( + "lno1zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszpgz5znzfgdzs".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); - } + // Malformed: unknown even TLV type 78 + assert_eq!( + "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpysgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)), + ); - #[test] - fn fails_parsing_bech32_encoded_offer_with_invalid_hrp() { - let encoded_offer = "lni1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg"; - match encoded_offer.parse::() { - Ok(_) => panic!("Valid offer: {}", encoded_offer), - Err(e) => assert_eq!(e, Bolt12ParseError::InvalidBech32Hrp), - } - } + // Malformed: empty + assert_eq!( + "lno1".parse::(), + Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)), + ); - #[test] - fn fails_parsing_bech32_encoded_offer_with_invalid_bech32_data() { - let encoded_offer = "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxo"; - match encoded_offer.parse::() { - Ok(_) => panic!("Valid offer: {}", encoded_offer), - Err(e) => assert_eq!(e, Bolt12ParseError::Bech32(bech32::Error::InvalidChar('o'))), - } - } + // Malformed: truncated at type + assert_eq!( + "lno1pg".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); - #[test] - fn fails_parsing_bech32_encoded_offer_with_invalid_tlv_data() { - let encoded_offer = "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxgqqqqq"; - match encoded_offer.parse::() { - Ok(_) => panic!("Valid offer: {}", encoded_offer), - Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)), - } + // Malformed: truncated in length + assert_eq!( + "lno1pt7s".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: truncated after length + assert_eq!( + "lno1pgpq".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: truncated in description + assert_eq!( + "lno1pgpyz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: invalid offer_chains length + assert_eq!( + "lno1qgqszzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: truncated currency UTF-8 + assert_eq!( + "lno1qcqcqzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: invalid currency UTF-8 + assert_eq!( + "lno1qcpgqsg2q4q5cj2rg5tzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: truncated description UTF-8 + assert_eq!( + "lno1pgqcq93pqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqy".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); + + // Malformed: invalid description UTF-8 + assert_eq!( + "lno1pgpgqsgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); + + // Malformed: truncated offer_paths + assert_eq!( + "lno1pgz5znzfgdz3qqgpzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: zero num_hops in blinded_path + assert_eq!( + "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: truncated onionmsg_hop in blinded_path + assert_eq!( + "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: bad first_node_id in blinded_path + assert_eq!( + "lno1pgz5znzfgdz3qqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: bad blinding in blinded_path + assert_eq!( + "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcpqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: bad blinded_node_id in onionmsg_hop + assert_eq!( + "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::ShortRead)), + ); + + // Malformed: truncated issuer UTF-8 + assert_eq!( + "lno1pgz5znzfgdz3yqvqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); + + // Malformed: invalid issuer UTF-8 + assert_eq!( + "lno1pgz5znzfgdz3yq5qgytzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); + + // Malformed: invalid offer_node_id + assert_eq!( + "lno1pgz5znzfgdz3vggzqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvps".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); + + // Contains type >= 80 + assert_eq!( + "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::(), + Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), + ); + + // TODO: Resolved in spec https://github.com/lightning/bolts/pull/798/files#r1334851959 + // Contains unknown feature 22 + assert!( + "lno1pgx9getnwss8vetrw3hhyucvqdqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::().is_ok() + ); + + // Missing offer_description + assert_eq!( + "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese".parse::(), + Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)), + ); + + // Missing offer_node_id" + assert_eq!( + "lno1pgx9getnwss8vetrw3hhyuc".parse::(), + Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)), + ); } }