X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Fparse.rs;h=400cf51a2944d47cc0afd19e227acb3216242959;hb=fb670c8faae8c1e990496b869e62dfbde10a64f8;hp=e9477086ee981358aea419416e676e4b682c1fc1;hpb=d7e3320c030654ca3ff2b6ffd83ae65dfbe5e3c8;p=rust-lightning diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index e9477086..400cf51a 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -179,6 +179,8 @@ pub enum Bolt12SemanticError { MissingPayerMetadata, /// A payer id was expected but was missing. MissingPayerId, + /// The payment id for a refund or request is already in use. + DuplicatePaymentId, /// Blinded paths were expected but were missing. MissingPaths, /// The blinded payinfo given does not match the number of blinded path hops. @@ -214,3 +216,93 @@ impl From for Bolt12ParseError { Self::InvalidSignature(error) } } + +#[cfg(test)] +mod bolt12_tests { + use super::Bolt12ParseError; + use crate::offers::offer::Offer; + + #[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 = [ + // A complete string is valid + "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", + + // + can join anywhere + "l+no1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg", + + // Multiple + can join + "lno1pqps7sjqpgt+yzm3qv4uxzmtsd3jjqer9wd3hy6tsw3+5k7msjzfpy7nz5yqcn+ygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd+5xvxg", + + // + can be followed by whitespace + "lno1pqps7sjqpgt+ yzm3qv4uxzmtsd3jjqer9wd3hy6tsw3+ 5k7msjzfpy7nz5yqcn+\nygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd+\r\n 5xvxg", + ]; + for encoded_offer in &offers { + if let Err(e) = encoded_offer.parse::() { + panic!("Invalid offer ({:?}): {}", e, encoded_offer); + } + } + } + + #[test] + fn fails_parsing_bech32_encoded_offers_with_invalid_continuations() { + let offers = [ + // + must be surrounded by bech32 characters + "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), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::Bolt12ParseError; + use bitcoin::bech32; + use crate::ln::msgs::DecodeError; + use crate::offers::offer::Offer; + + #[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), + } + } + + #[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'))), + } + } + + #[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)), + } + } +}