1use alloc::string;
2#[cfg(not(feature = "std"))]
3use core::convert::TryFrom;
4use core::fmt;
5use core::fmt::{Display, Formatter};
6use core::num::ParseIntError;
7use core::str::FromStr;
8#[cfg(feature = "std")]
9use std::error;
10
11use bech32::primitives::decode::{CheckedHrpstring, CheckedHrpstringError};
12use bech32::{Fe32, Fe32IterExt};
13
14use crate::prelude::*;
15use crate::Bolt11Bech32;
16use bitcoin::hashes::sha256;
17use bitcoin::hashes::Hash;
18use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
19use lightning_types::payment::PaymentSecret;
20use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
21
22use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
23use bitcoin::secp256k1::PublicKey;
24
25use super::{
26 constants, Bolt11Invoice, Bolt11InvoiceFeatures, Bolt11InvoiceSignature, Bolt11ParseError,
27 Bolt11SemanticError, Currency, Description, ExpiryTime, Fallback, MinFinalCltvExpiryDelta,
28 ParseOrSemanticError, PayeePubKey, PositiveTimestamp, PrivateRoute, RawBolt11Invoice,
29 RawDataPart, RawHrp, RawTaggedField, Sha256, SiPrefix, SignedRawBolt11Invoice, TaggedField,
30};
31
32use self::hrp_sm::parse_hrp;
33
34pub trait FromBase32: Sized {
36 type Err;
38
39 fn from_base32(b32: &[Fe32]) -> Result<Self, Self::Err>;
41}
42
43impl FromBase32 for Vec<u8> {
46 type Err = Bolt11ParseError;
47
48 fn from_base32(data: &[Fe32]) -> Result<Self, Self::Err> {
49 Ok(data.iter().copied().fes_to_bytes().collect::<Self>())
50 }
51}
52
53impl<const N: usize> FromBase32 for [u8; N] {
54 type Err = Bolt11ParseError;
55
56 fn from_base32(data: &[Fe32]) -> Result<Self, Self::Err> {
57 let mut res_arr = [0; N];
58 let mut count = 0;
60 for elem in data.iter().copied().fes_to_bytes() {
61 if count >= N {
62 count += 1;
64 break;
65 }
66 res_arr[count] = elem;
67 count += 1;
68 }
69 if count != N {
70 return Err(Bolt11ParseError::InvalidSliceLength(count, N, "<[u8; N]>"));
71 }
72 Ok(res_arr)
73 }
74}
75
76impl FromBase32 for PaymentSecret {
77 type Err = Bolt11ParseError;
78
79 fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
80 if field_data.len() != 52 {
81 return Err(Bolt11ParseError::InvalidSliceLength(
82 field_data.len(),
83 52,
84 "PaymentSecret",
85 ));
86 }
87 let data_bytes = <[u8; 32]>::from_base32(field_data)?;
88 Ok(PaymentSecret(data_bytes))
89 }
90}
91
92impl FromBase32 for Bolt11InvoiceFeatures {
93 type Err = Bolt11ParseError;
94
95 fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
101 let mut carry_bits = 0;
104 let mut carry = 0u8;
105 let expected_raw_length = (field_data.len() * 5 + 7) / 8;
106 let mut output = Vec::<u8>::with_capacity(expected_raw_length);
107
108 for curr_in in field_data.iter().rev() {
110 let curr_in_as_u8 = curr_in.to_u8();
111 if carry_bits >= 3 {
112 let next = carry + (curr_in_as_u8 << carry_bits);
115 output.push(next);
116 carry = curr_in_as_u8 >> (8 - carry_bits);
117 carry_bits -= 3; } else {
119 carry += curr_in_as_u8 << carry_bits;
121 carry_bits += 5;
122 }
123 }
124
125 if carry_bits > 0 {
127 output.push(carry);
128 }
129
130 debug_assert_eq!(output.len(), expected_raw_length);
133
134 while !output.is_empty() && output[output.len() - 1] == 0 {
136 output.pop();
137 }
138
139 Ok(Bolt11InvoiceFeatures::from_le_bytes(output))
140 }
141}
142
143mod hrp_sm {
145 use core::ops::Range;
146
147 #[derive(PartialEq, Eq, Debug)]
148 enum States {
149 Start,
150 ParseL,
151 ParseN,
152 ParseCurrencyPrefix,
153 ParseAmountNumber,
154 ParseAmountSiPrefix,
155 }
156
157 impl States {
158 fn next_state(&self, read_byte: u8) -> Result<States, super::Bolt11ParseError> {
159 let read_symbol = match char::from_u32(read_byte.into()) {
160 Some(symb) if symb.is_ascii() => symb,
161 _ => return Err(super::Bolt11ParseError::MalformedHRP),
162 };
163 match *self {
164 States::Start => {
165 if read_symbol == 'l' {
166 Ok(States::ParseL)
167 } else {
168 Err(super::Bolt11ParseError::MalformedHRP)
169 }
170 },
171 States::ParseL => {
172 if read_symbol == 'n' {
173 Ok(States::ParseN)
174 } else {
175 Err(super::Bolt11ParseError::MalformedHRP)
176 }
177 },
178 States::ParseN => {
179 if !read_symbol.is_numeric() {
180 Ok(States::ParseCurrencyPrefix)
181 } else {
182 Ok(States::ParseAmountNumber)
183 }
184 },
185 States::ParseCurrencyPrefix => {
186 if !read_symbol.is_numeric() {
187 Ok(States::ParseCurrencyPrefix)
188 } else {
189 Ok(States::ParseAmountNumber)
190 }
191 },
192 States::ParseAmountNumber => {
193 if read_symbol.is_numeric() {
194 Ok(States::ParseAmountNumber)
195 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
196 Ok(States::ParseAmountSiPrefix)
197 } else {
198 Err(super::Bolt11ParseError::UnknownSiPrefix)
199 }
200 },
201 States::ParseAmountSiPrefix => Err(super::Bolt11ParseError::MalformedHRP),
202 }
203 }
204
205 fn is_final(&self) -> bool {
206 !(*self == States::ParseL || *self == States::ParseN)
207 }
208 }
209
210 struct StateMachine {
211 state: States,
212 position: usize,
213 currency_prefix: Option<Range<usize>>,
214 amount_number: Option<Range<usize>>,
215 amount_si_prefix: Option<Range<usize>>,
216 }
217
218 impl StateMachine {
219 fn new() -> StateMachine {
220 StateMachine {
221 state: States::Start,
222 position: 0,
223 currency_prefix: None,
224 amount_number: None,
225 amount_si_prefix: None,
226 }
227 }
228
229 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
230 let new_range = match *range {
231 None => Range { start: position, end: position + 1 },
232 Some(ref r) => Range { start: r.start, end: r.end + 1 },
233 };
234 *range = Some(new_range);
235 }
236
237 fn step(&mut self, c: u8) -> Result<(), super::Bolt11ParseError> {
238 let next_state = self.state.next_state(c)?;
239 match next_state {
240 States::ParseCurrencyPrefix => {
241 StateMachine::update_range(&mut self.currency_prefix, self.position)
242 },
243 States::ParseAmountNumber => {
244 StateMachine::update_range(&mut self.amount_number, self.position)
245 },
246 States::ParseAmountSiPrefix => {
247 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
248 },
249 _ => {},
250 }
251
252 self.position += 1;
253 self.state = next_state;
254 Ok(())
255 }
256
257 fn is_final(&self) -> bool {
258 self.state.is_final()
259 }
260
261 fn currency_prefix(&self) -> &Option<Range<usize>> {
262 &self.currency_prefix
263 }
264
265 fn amount_number(&self) -> &Option<Range<usize>> {
266 &self.amount_number
267 }
268
269 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
270 &self.amount_si_prefix
271 }
272 }
273
274 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> {
275 let mut sm = StateMachine::new();
276 for c in input.bytes() {
277 sm.step(c)?;
278 }
279
280 if !sm.is_final() {
281 return Err(super::Bolt11ParseError::MalformedHRP);
282 }
283
284 let currency = sm.currency_prefix().clone().map(|r| &input[r]).unwrap_or("");
285 let amount = sm.amount_number().clone().map(|r| &input[r]).unwrap_or("");
286 let si = sm.amount_si_prefix().clone().map(|r| &input[r]).unwrap_or("");
287
288 Ok((currency, amount, si))
289 }
290}
291
292impl FromStr for super::Currency {
293 type Err = Bolt11ParseError;
294
295 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
296 match currency_prefix {
297 "bc" => Ok(Currency::Bitcoin),
298 "tb" => Ok(Currency::BitcoinTestnet),
299 "bcrt" => Ok(Currency::Regtest),
300 "sb" => Ok(Currency::Simnet),
301 "tbs" => Ok(Currency::Signet),
302 _ => Err(Bolt11ParseError::UnknownCurrency),
303 }
304 }
305}
306
307impl FromStr for SiPrefix {
308 type Err = Bolt11ParseError;
309
310 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
311 use crate::SiPrefix::*;
312 match currency_prefix {
313 "m" => Ok(Milli),
314 "u" => Ok(Micro),
315 "n" => Ok(Nano),
316 "p" => Ok(Pico),
317 _ => Err(Bolt11ParseError::UnknownSiPrefix),
318 }
319 }
320}
321
322impl FromStr for Bolt11Invoice {
341 type Err = ParseOrSemanticError;
342
343 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
344 let signed = s.parse::<SignedRawBolt11Invoice>()?;
345 Ok(Bolt11Invoice::from_signed(signed)?)
346 }
347}
348
349impl FromStr for SignedRawBolt11Invoice {
378 type Err = Bolt11ParseError;
379
380 fn from_str(s: &str) -> Result<Self, Self::Err> {
381 let parsed = CheckedHrpstring::new::<Bolt11Bech32>(s)?;
382 let hrp = parsed.hrp();
383 let data: Vec<_> = parsed.fe32_iter::<&mut dyn Iterator<Item = u8>>().collect();
386
387 const SIGNATURE_LEN_5: usize = 104; if data.len() < SIGNATURE_LEN_5 {
389 return Err(Bolt11ParseError::TooShortDataPart);
390 }
391
392 let raw_hrp: RawHrp = hrp.to_string().to_lowercase().parse()?;
393 let data_part = RawDataPart::from_base32(&data[..data.len() - SIGNATURE_LEN_5])?;
394 let raw_invoice = RawBolt11Invoice { hrp: raw_hrp, data: data_part };
395 let hash = raw_invoice.signable_hash();
396
397 Ok(SignedRawBolt11Invoice {
398 raw_invoice,
399 hash,
400 signature: Bolt11InvoiceSignature::from_base32(&data[data.len() - SIGNATURE_LEN_5..])?,
401 })
402 }
403}
404
405impl FromStr for RawHrp {
406 type Err = Bolt11ParseError;
407
408 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
409 let parts = parse_hrp(hrp)?;
410
411 let currency = parts.0.parse::<Currency>()?;
412
413 let amount = if !parts.1.is_empty() { Some(parts.1.parse::<u64>()?) } else { None };
414
415 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
416 None
417 } else {
418 let si: SiPrefix = parts.2.parse()?;
419 if let Some(amt) = amount {
420 if amt.checked_mul(si.multiplier()).is_none() {
421 return Err(Bolt11ParseError::IntegerOverflowError);
422 }
423 }
424 Some(si)
425 };
426
427 Ok(RawHrp { currency, raw_amount: amount, si_prefix })
428 }
429}
430
431impl FromBase32 for RawDataPart {
432 type Err = Bolt11ParseError;
433
434 fn from_base32(data: &[Fe32]) -> Result<Self, Self::Err> {
435 const TIMESTAMP_LEN: usize = 7;
436 if data.len() < TIMESTAMP_LEN {
437 return Err(Bolt11ParseError::TooShortDataPart);
438 }
439
440 let timestamp = PositiveTimestamp::from_base32(&data[0..TIMESTAMP_LEN])?;
441 let tagged = parse_tagged_parts(&data[TIMESTAMP_LEN..])?;
442
443 Ok(RawDataPart { timestamp, tagged_fields: tagged })
444 }
445}
446
447impl FromBase32 for PositiveTimestamp {
448 type Err = Bolt11ParseError;
449
450 fn from_base32(b32: &[Fe32]) -> Result<Self, Self::Err> {
451 if b32.len() != 7 {
452 return Err(Bolt11ParseError::InvalidSliceLength(b32.len(), 7, "PositiveTimestamp"));
453 }
454 let timestamp: u64 = parse_u64_be(b32).expect("7*5bit < 64bit, no overflow possible");
455 match PositiveTimestamp::from_unix_timestamp(timestamp) {
456 Ok(t) => Ok(t),
457 Err(_) => unreachable!(),
458 }
459 }
460}
461
462impl FromBase32 for Bolt11InvoiceSignature {
463 type Err = Bolt11ParseError;
464 fn from_base32(signature: &[Fe32]) -> Result<Self, Self::Err> {
465 if signature.len() != 104 {
466 return Err(Bolt11ParseError::InvalidSliceLength(
467 signature.len(),
468 104,
469 "Bolt11InvoiceSignature",
470 ));
471 }
472 let recoverable_signature_bytes = <[u8; 65]>::from_base32(signature)?;
473 let signature = &recoverable_signature_bytes[0..64];
474 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
475
476 Ok(Bolt11InvoiceSignature(RecoverableSignature::from_compact(signature, recovery_id)?))
477 }
478}
479
480macro_rules! define_parse_int_be {
481 ($name: ident, $ty: ty) => {
482 fn $name(digits: &[Fe32]) -> Option<$ty> {
483 digits.iter().fold(Some(Default::default()), |acc, b| {
484 acc.and_then(|x| x.checked_mul(32))
485 .and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
486 })
487 }
488 };
489}
490define_parse_int_be!(parse_u16_be, u16);
491define_parse_int_be!(parse_u64_be, u64);
492
493fn parse_tagged_parts(data: &[Fe32]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
494 let mut parts = Vec::<RawTaggedField>::new();
495 let mut data = data;
496
497 while !data.is_empty() {
498 if data.len() < 3 {
499 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
500 }
501
502 let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize;
505 let last_element = 3 + len;
506
507 if data.len() < last_element {
508 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
509 }
510
511 let field = &data[0..last_element];
513
514 data = &data[last_element..];
516
517 match TaggedField::from_base32(field) {
518 Ok(field) => parts.push(RawTaggedField::KnownSemantics(field)),
519 Err(Bolt11ParseError::Skip)
520 | Err(Bolt11ParseError::InvalidSliceLength(_, _, _))
521 | Err(Bolt11ParseError::Bech32Error(_)) => {
522 parts.push(RawTaggedField::UnknownSemantics(field.into()))
523 },
524 Err(e) => return Err(e),
525 }
526 }
527 Ok(parts)
528}
529
530impl FromBase32 for TaggedField {
531 type Err = Bolt11ParseError;
532
533 fn from_base32(field: &[Fe32]) -> Result<TaggedField, Bolt11ParseError> {
534 if field.len() < 3 {
535 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
536 }
537
538 let tag = field[0];
539 let field_data = &field[3..];
540
541 match tag.to_u8() {
542 constants::TAG_PAYMENT_HASH => {
543 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?))
544 },
545 constants::TAG_DESCRIPTION => {
546 Ok(TaggedField::Description(Description::from_base32(field_data)?))
547 },
548 constants::TAG_PAYEE_PUB_KEY => {
549 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?))
550 },
551 constants::TAG_DESCRIPTION_HASH => {
552 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?))
553 },
554 constants::TAG_EXPIRY_TIME => {
555 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?))
556 },
557 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA => Ok(TaggedField::MinFinalCltvExpiryDelta(
558 MinFinalCltvExpiryDelta::from_base32(field_data)?,
559 )),
560 constants::TAG_FALLBACK => {
561 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?))
562 },
563 constants::TAG_PRIVATE_ROUTE => {
564 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?))
565 },
566 constants::TAG_PAYMENT_SECRET => {
567 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?))
568 },
569 constants::TAG_PAYMENT_METADATA => {
570 Ok(TaggedField::PaymentMetadata(Vec::<u8>::from_base32(field_data)?))
571 },
572 constants::TAG_FEATURES => {
573 Ok(TaggedField::Features(Bolt11InvoiceFeatures::from_base32(field_data)?))
574 },
575 _ => {
576 Err(Bolt11ParseError::Skip)
578 },
579 }
580 }
581}
582
583impl FromBase32 for Sha256 {
584 type Err = Bolt11ParseError;
585
586 fn from_base32(field_data: &[Fe32]) -> Result<Sha256, Bolt11ParseError> {
587 if field_data.len() != 52 {
588 Err(Bolt11ParseError::Skip)
590 } else {
591 Ok(Sha256(
592 sha256::Hash::from_slice(&<[u8; 32]>::from_base32(field_data)?)
593 .expect("length was checked before (52 u5 -> 32 u8)"),
594 ))
595 }
596 }
597}
598
599impl FromBase32 for Description {
600 type Err = Bolt11ParseError;
601
602 fn from_base32(field_data: &[Fe32]) -> Result<Description, Bolt11ParseError> {
603 let bytes = Vec::<u8>::from_base32(field_data)?;
604 let description = String::from_utf8(bytes)?;
605 Ok(Description::new(description)
606 .expect("Max len is 639=floor(1023*5/8) since the len field is only 10bits long"))
607 }
608}
609
610impl FromBase32 for PayeePubKey {
611 type Err = Bolt11ParseError;
612
613 fn from_base32(field_data: &[Fe32]) -> Result<PayeePubKey, Bolt11ParseError> {
614 if field_data.len() != 53 {
615 Err(Bolt11ParseError::Skip)
617 } else {
618 let data_bytes = <[u8; 33]>::from_base32(field_data)?;
619 let pub_key = PublicKey::from_slice(&data_bytes)?;
620 Ok(pub_key.into())
621 }
622 }
623}
624
625impl FromBase32 for ExpiryTime {
626 type Err = Bolt11ParseError;
627
628 fn from_base32(field_data: &[Fe32]) -> Result<ExpiryTime, Bolt11ParseError> {
629 match parse_u64_be(field_data).map(ExpiryTime::from_seconds) {
630 Some(t) => Ok(t),
631 None => Err(Bolt11ParseError::IntegerOverflowError),
632 }
633 }
634}
635
636impl FromBase32 for MinFinalCltvExpiryDelta {
637 type Err = Bolt11ParseError;
638
639 fn from_base32(field_data: &[Fe32]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
640 let expiry = parse_u64_be(field_data);
641 if let Some(expiry) = expiry {
642 Ok(MinFinalCltvExpiryDelta(expiry))
643 } else {
644 Err(Bolt11ParseError::IntegerOverflowError)
645 }
646 }
647}
648
649impl FromBase32 for Fallback {
650 type Err = Bolt11ParseError;
651
652 fn from_base32(field_data: &[Fe32]) -> Result<Fallback, Bolt11ParseError> {
653 if field_data.is_empty() {
654 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
655 }
656
657 let version = field_data[0].to_u8();
658 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
659
660 match version {
661 0..=16 => {
662 if bytes.len() < 2 || bytes.len() > 40 {
663 return Err(Bolt11ParseError::InvalidSegWitProgramLength);
664 }
665 let version = WitnessVersion::try_from(version)
666 .expect("0 through 16 are valid SegWit versions");
667 Ok(Fallback::SegWitProgram { version, program: bytes })
668 },
669 17 => {
670 let pkh = match PubkeyHash::from_slice(&bytes) {
671 Ok(pkh) => pkh,
672 Err(_) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
673 };
674 Ok(Fallback::PubKeyHash(pkh))
675 },
676 18 => {
677 let sh = match ScriptHash::from_slice(&bytes) {
678 Ok(sh) => sh,
679 Err(_) => return Err(Bolt11ParseError::InvalidScriptHashLength),
680 };
681 Ok(Fallback::ScriptHash(sh))
682 },
683 _ => Err(Bolt11ParseError::Skip),
684 }
685 }
686}
687
688impl FromBase32 for PrivateRoute {
689 type Err = Bolt11ParseError;
690
691 fn from_base32(field_data: &[Fe32]) -> Result<PrivateRoute, Bolt11ParseError> {
692 let bytes = Vec::<u8>::from_base32(field_data)?;
693
694 if bytes.len() % 51 != 0 {
695 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
696 }
697
698 let mut route_hops = Vec::with_capacity(bytes.len() / 51);
699
700 let mut bytes = bytes.as_slice();
701 while !bytes.is_empty() {
702 let hop_bytes = &bytes[0..51];
703 bytes = &bytes[51..];
704
705 let mut channel_id: [u8; 8] = Default::default();
706 channel_id.copy_from_slice(&hop_bytes[33..41]);
707
708 let hop = RouteHintHop {
709 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
710 short_channel_id: u64::from_be_bytes(channel_id),
711 fees: RoutingFees {
712 base_msat: u32::from_be_bytes(
713 hop_bytes[41..45].try_into().expect("slice too big?"),
714 ),
715 proportional_millionths: u32::from_be_bytes(
716 hop_bytes[45..49].try_into().expect("slice too big?"),
717 ),
718 },
719 cltv_expiry_delta: u16::from_be_bytes(
720 hop_bytes[49..51].try_into().expect("slice too big?"),
721 ),
722 htlc_minimum_msat: None,
723 htlc_maximum_msat: None,
724 };
725
726 route_hops.push(hop);
727 }
728
729 Ok(PrivateRoute(RouteHint(route_hops)))
730 }
731}
732
733impl Display for Bolt11ParseError {
734 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
735 match *self {
736 Bolt11ParseError::Bech32Error(ref e) => {
738 write!(f, "Invalid bech32: {}", e)
739 },
740 Bolt11ParseError::ParseAmountError(ref e) => {
741 write!(f, "Invalid amount in hrp ({})", e)
742 },
743 Bolt11ParseError::MalformedSignature(ref e) => {
744 write!(f, "Invalid secp256k1 signature: {}", e)
745 },
746 Bolt11ParseError::DescriptionDecodeError(ref e) => {
747 write!(f, "Description is not a valid utf-8 string: {}", e)
748 },
749 Bolt11ParseError::InvalidSliceLength(ref len, ref expected, ref elemen) => {
750 write!(f, "Slice had length {} instead of {} for element {}", len, expected, elemen)
751 },
752 Bolt11ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
753 Bolt11ParseError::UnknownCurrency => f.write_str("currency code unknown"),
754 Bolt11ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
755 Bolt11ParseError::MalformedHRP => f.write_str("malformed human readable part"),
756 Bolt11ParseError::TooShortDataPart => {
757 f.write_str("data part too short (should be at least 111 bech32 chars long)")
758 },
759 Bolt11ParseError::UnexpectedEndOfTaggedFields => {
760 f.write_str("tagged fields part ended unexpectedly")
761 },
762 Bolt11ParseError::PaddingError => f.write_str("some data field had bad padding"),
763 Bolt11ParseError::IntegerOverflowError => {
764 f.write_str("parsed integer doesn't fit into receiving type")
765 },
766 Bolt11ParseError::InvalidSegWitProgramLength => {
767 f.write_str("fallback SegWit program is too long or too short")
768 },
769 Bolt11ParseError::InvalidPubKeyHashLength => {
770 f.write_str("fallback public key hash has a length unequal 20 bytes")
771 },
772 Bolt11ParseError::InvalidScriptHashLength => {
773 f.write_str("fallback script hash has a length unequal 32 bytes")
774 },
775 Bolt11ParseError::Skip => f.write_str(
776 "the tagged field has to be skipped because of an unexpected, but allowed property",
777 ),
778 }
779 }
780}
781
782impl Display for ParseOrSemanticError {
783 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
784 match self {
785 ParseOrSemanticError::ParseError(err) => err.fmt(f),
786 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
787 }
788 }
789}
790
791#[cfg(feature = "std")]
792impl error::Error for Bolt11ParseError {}
793
794#[cfg(feature = "std")]
795impl error::Error for ParseOrSemanticError {}
796
797macro_rules! from_error {
798 ($my_error:expr, $extern_error:ty) => {
799 impl From<$extern_error> for Bolt11ParseError {
800 fn from(e: $extern_error) -> Self {
801 $my_error(e)
802 }
803 }
804 };
805}
806
807from_error!(Bolt11ParseError::MalformedSignature, bitcoin::secp256k1::Error);
808from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
809from_error!(Bolt11ParseError::DescriptionDecodeError, string::FromUtf8Error);
810
811impl From<CheckedHrpstringError> for Bolt11ParseError {
812 fn from(e: CheckedHrpstringError) -> Self {
813 Self::Bech32Error(e)
814 }
815}
816
817impl From<Bolt11ParseError> for ParseOrSemanticError {
818 fn from(e: Bolt11ParseError) -> Self {
819 ParseOrSemanticError::ParseError(e)
820 }
821}
822
823impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
824 fn from(e: Bolt11SemanticError) -> Self {
825 ParseOrSemanticError::SemanticError(e)
826 }
827}
828
829#[cfg(test)]
830mod test {
831 use super::FromBase32;
832 use crate::de::Bolt11ParseError;
833 use bech32::Fe32;
834 use bitcoin::hashes::sha256;
835 use bitcoin::secp256k1::PublicKey;
836 use std::str::FromStr;
837
838 const CHARSET_REV: [i8; 128] = [
839 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
840 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
841 -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13,
842 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1,
843 -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28,
844 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
845 ];
846
847 fn from_bech32(bytes_5b: &[u8]) -> Vec<Fe32> {
848 bytes_5b.iter().map(|c| Fe32::try_from(CHARSET_REV[*c as usize] as u8).unwrap()).collect()
849 }
850
851 #[test]
852 fn test_parse_currency_prefix() {
853 use crate::Currency;
854
855 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
856 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
857 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
858 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
859 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
860 assert_eq!("something_else".parse::<Currency>(), Err(Bolt11ParseError::UnknownCurrency))
861 }
862
863 #[test]
864 fn test_parse_int_from_bytes_be() {
865 use crate::de::parse_u16_be;
866
867 assert_eq!(
868 parse_u16_be(&[
869 Fe32::try_from(1).unwrap(),
870 Fe32::try_from(2).unwrap(),
871 Fe32::try_from(3).unwrap(),
872 Fe32::try_from(4).unwrap(),
873 ]),
874 Some(34916)
875 );
876 assert_eq!(
877 parse_u16_be(&[
878 Fe32::try_from(2).unwrap(),
879 Fe32::try_from(0).unwrap(),
880 Fe32::try_from(0).unwrap(),
881 Fe32::try_from(0).unwrap(),
882 ]),
883 None
884 );
885 }
886
887 #[test]
888 fn test_parse_sha256_hash() {
889 use crate::Sha256;
890
891 let input = from_bech32("qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes());
892
893 let hash = sha256::Hash::from_str(
894 "0001020304050607080900010203040506070809000102030405060708090102",
895 )
896 .unwrap();
897 let expected = Ok(Sha256(hash));
898
899 assert_eq!(Sha256::from_base32(&input), expected);
900
901 let input_unexpected_length =
903 from_bech32("qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes());
904 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
905 }
906
907 #[test]
908 fn test_parse_description() {
909 use crate::Description;
910
911 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
912 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
913 assert_eq!(Description::from_base32(&input), expected);
914 }
915
916 #[test]
917 fn test_parse_payee_pub_key() {
918 use crate::PayeePubKey;
919
920 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
921 let pk_bytes = [
922 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
923 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
924 0x0f, 0x93, 0x4d, 0xd9, 0xad,
925 ];
926 let expected = Ok(PayeePubKey(PublicKey::from_slice(&pk_bytes[..]).unwrap()));
927
928 assert_eq!(PayeePubKey::from_base32(&input), expected);
929
930 let input_unexpected_length =
932 from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes());
933 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
934 }
935
936 #[test]
937 fn test_parse_expiry_time() {
938 use crate::ExpiryTime;
939
940 let input = from_bech32("pu".as_bytes());
941 let expected = Ok(ExpiryTime::from_seconds(60));
942 assert_eq!(ExpiryTime::from_base32(&input), expected);
943
944 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
945 assert_eq!(
946 ExpiryTime::from_base32(&input_too_large),
947 Err(Bolt11ParseError::IntegerOverflowError)
948 );
949 }
950
951 #[test]
952 fn test_parse_min_final_cltv_expiry_delta() {
953 use crate::MinFinalCltvExpiryDelta;
954
955 let input = from_bech32("pr".as_bytes());
956 let expected = Ok(MinFinalCltvExpiryDelta(35));
957
958 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
959 }
960
961 #[test]
962 fn test_parse_fallback() {
963 use crate::Fallback;
964 use bitcoin::hashes::Hash;
965 use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
966
967 let cases = vec![
968 (
969 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
970 Ok(Fallback::PubKeyHash(
971 PubkeyHash::from_slice(&[
972 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59,
973 0xd3, 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7,
974 ])
975 .unwrap(),
976 )),
977 ),
978 (
979 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
980 Ok(Fallback::ScriptHash(
981 ScriptHash::from_slice(&[
982 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9,
983 0xf3, 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45,
984 ])
985 .unwrap(),
986 )),
987 ),
988 (
989 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
990 Ok(Fallback::SegWitProgram {
991 version: WitnessVersion::V0,
992 program: Vec::from(
993 &[
994 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
995 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
996 ][..],
997 ),
998 }),
999 ),
1000 (vec![Fe32::try_from(21).unwrap(); 41], Err(Bolt11ParseError::Skip)),
1001 (vec![], Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)),
1002 (
1003 vec![Fe32::try_from(1).unwrap(); 81],
1004 Err(Bolt11ParseError::InvalidSegWitProgramLength),
1005 ),
1006 (vec![Fe32::try_from(17).unwrap(); 1], Err(Bolt11ParseError::InvalidPubKeyHashLength)),
1007 (vec![Fe32::try_from(18).unwrap(); 1], Err(Bolt11ParseError::InvalidScriptHashLength)),
1008 ];
1009
1010 for (input, expected) in cases.into_iter() {
1011 assert_eq!(Fallback::from_base32(&input), expected);
1012 }
1013 }
1014
1015 #[test]
1016 fn test_parse_route() {
1017 use crate::PrivateRoute;
1018 use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
1019
1020 let input = from_bech32(
1021 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
1022 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
1023 );
1024
1025 let expected = vec![
1026 RouteHintHop {
1027 src_node_id: PublicKey::from_slice(
1028 &[
1029 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
1030 0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
1031 0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55,
1032 ][..],
1033 )
1034 .unwrap(),
1035 short_channel_id: 0x0102030405060708,
1036 fees: RoutingFees { base_msat: 1, proportional_millionths: 20 },
1037 cltv_expiry_delta: 3,
1038 htlc_minimum_msat: None,
1039 htlc_maximum_msat: None,
1040 },
1041 RouteHintHop {
1042 src_node_id: PublicKey::from_slice(
1043 &[
1044 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
1045 0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
1046 0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55,
1047 ][..],
1048 )
1049 .unwrap(),
1050 short_channel_id: 0x030405060708090a,
1051 fees: RoutingFees { base_msat: 2, proportional_millionths: 30 },
1052 cltv_expiry_delta: 4,
1053 htlc_minimum_msat: None,
1054 htlc_maximum_msat: None,
1055 },
1056 ];
1057
1058 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
1059
1060 assert_eq!(
1061 PrivateRoute::from_base32(&[Fe32::try_from(0).unwrap(); 40][..]),
1062 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
1063 );
1064 }
1065
1066 #[test]
1067 fn test_payment_secret_and_features_de_and_ser() {
1068 use crate::TaggedField::*;
1069 use crate::{
1070 Bolt11InvoiceSignature, Currency, PositiveTimestamp, RawBolt11Invoice, RawDataPart,
1071 RawHrp, Sha256, SiPrefix, SignedRawBolt11Invoice,
1072 };
1073 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1074 use lightning_types::features::Bolt11InvoiceFeatures;
1075
1076 let expected_features =
1078 Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
1079 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
1080 let invoice =
1081 SignedRawBolt11Invoice {
1082 raw_invoice: RawBolt11Invoice {
1083 hrp: RawHrp {
1084 currency: Currency::Bitcoin,
1085 raw_amount: Some(25),
1086 si_prefix: Some(SiPrefix::Milli),
1087 },
1088 data: RawDataPart {
1089 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1090 tagged_fields: vec ! [
1091 PaymentHash(Sha256(sha256::Hash::from_str(
1092 "0001020304050607080900010203040506070809000102030405060708090102"
1093 ).unwrap())).into(),
1094 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
1095 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1096 Features(expected_features).into()],
1097 },
1098 },
1099 hash: [
1100 0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1101 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1102 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9,
1103 ],
1104 signature: Bolt11InvoiceSignature(
1105 RecoverableSignature::from_compact(
1106 &[
1107 0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf,
1108 0x68, 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64,
1109 0xd3, 0x60, 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab,
1110 0x4c, 0x85, 0xd3, 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08,
1111 0x12, 0xf9, 0x5d, 0x97, 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda,
1112 0xe0, 0x1a, 0xf3, 0xc1,
1113 ],
1114 RecoveryId::from_i32(1).unwrap(),
1115 )
1116 .unwrap(),
1117 ),
1118 };
1119 assert_eq!(invoice_str, invoice.to_string());
1120 assert_eq!(invoice_str.parse(), Ok(invoice));
1121 }
1122
1123 #[test]
1124 fn test_raw_signed_invoice_deserialization() {
1125 use crate::TaggedField::*;
1126 use crate::{
1127 Bolt11InvoiceSignature, Currency, PositiveTimestamp, RawBolt11Invoice, RawDataPart,
1128 RawHrp, Sha256, SignedRawBolt11Invoice,
1129 };
1130 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1131
1132 assert_eq!(
1133 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1134 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1135 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1136 Ok(SignedRawBolt11Invoice {
1137 raw_invoice: RawBolt11Invoice {
1138 hrp: RawHrp {
1139 currency: Currency::Bitcoin,
1140 raw_amount: None,
1141 si_prefix: None,
1142 },
1143 data: RawDataPart {
1144 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1145 tagged_fields: vec ! [
1146 PaymentHash(Sha256(sha256::Hash::from_str(
1147 "0001020304050607080900010203040506070809000102030405060708090102"
1148 ).unwrap())).into(),
1149 Description(
1150 crate::Description::new(
1151 "Please consider supporting this project".to_owned()
1152 ).unwrap()
1153 ).into(),
1154 ],
1155 },
1156 },
1157 hash: [
1158 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1159 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1160 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1161 ],
1162 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1163 & [
1164 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1165 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1166 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1167 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1168 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1169 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1170 ],
1171 RecoveryId::from_i32(0).unwrap()
1172 ).unwrap()),
1173 }
1174 )
1175 )
1176 }
1177
1178 #[test]
1181 fn test_deser_long_test_vectors() {
1182 use crate::Bolt11Invoice;
1183
1184 #[track_caller]
1185 fn parse_ok(invoice_str: &str) {
1186 let invoice = Bolt11Invoice::from_str(invoice_str).unwrap();
1187 let invoice_str2 = invoice.to_string();
1188 if invoice_str != invoice_str2 {
1189 panic!(
1190 "Invoice does not roundtrip: invoice_str != invoice_str2\n\
1191 invoice_str: {invoice_str}\n\
1192 invoice_str2: {invoice_str2}\n\
1193 \n\
1194 {invoice:?}"
1195 );
1196 }
1197 }
1198
1199 parse_ok(
1201 "lnbc10000000000000000010p1qqqqqqqdtuxpqkzq8sjzqgps4pvyczqq8sjzqgpuysszq0pyyqsrp2zs0sjz\
1202 qgps4pxrcfpqyqc2slpyyqsqsv9gwz59s5zqpqyps5rc9qsrs2pqxz5ysyzcfqgysyzs0sjzqgqq8sjzqgps4p\
1203 xqqzps4pqpssqgzpxps5ruysszqrps4pg8p2zgpsc2snpuysszqzqsgqvys0pyyqsrcfpqyqvycv9gfqqrcfpq\
1204 yq7zggpq8q5zqyruysszqwpgyqxpsjqsgq7zggpqps7zggpq8sjzqgqgqq7zggpqpq7zggpq8q5zqqpuysszq0\
1205 pyyqsqs0pyyqspsnqgzpqpqlpyyqsqszpuysszqyzvzpvysrqq8sjzqgqvrp7zggpqpqxpsspp5mf45hs3cgph\
1206 h0074r5qmr74y82r26ac4pzdg4nd9mdmsvz6ffqpssp5vr4yra4pcv74h9hk3d0233nqu4gktpuykjamrafrdp\
1207 uedqugzh3q9q2sqqqqqysgqcqrpqqxq8pqqqqqqnp4qgvcxpme2q5lng36j9gruwlrtk2f86s3c5xmk87yhvyu\
1208 wdeh025q5r9yqwnqegv9hj9nzkhyxaeyq92wcrnqp36pyrc2qzrvswj5g96ey2dn6qqqqqqqqqqqqqqqqqqqqq\
1209 qqqqqqqqp9a5vs0t4z56p64xyma8s84yvdx7uhqj0gvrr424fea2wpztq2fwqqqqqqqqqqqqqqqqqqqqqqqqqq\
1210 qqqqmy9qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
1211 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpcnsxc32du9n7amlypuhclzqrt6lkegq\
1212 0v3r7nczjv9tv30z7phq80r3dm7pvgykl7gwuenmem93h5xwdwac6ngsmzqc34khrg3qjgsq6qk6lc"
1213 );
1214 parse_ok(
1216 "lnbc8735500635020489010p1av5kfs8deupvyk4u5ynj03hmalhhhml0fxc2jlrv9z4lg6s4hnhkz69malhhe\
1217 t3x9yqpsxru4a3kwar2qtu2q2ughx367q600s5x7c7tln4k0fu78skxqevaqm8sayhuur377zgf3uf94n57xzh\
1218 dw99u42hwc089djn5xj723w7zageflsnzdmyte89tecf2ac7xhg4y3u9f4xpuv2hwxjlsarp0e24fu8tme6rgv\
1219 0tqj08z9f4u30rw59k8emhtvs7wye0xfw6x5q5tju2p208rvtkunzwtwghtp22tlnh62gxwhfkxp4cnz7ts3rx\
1220 vlzszhv9y00h77lpdvcjyhjtmalh5dn5e8n5w8cqle0vunzduu4nza9y0734qhxday9hzywl0aa0vhzy0qmphc\
1221 64d4hduj08dv2krpgqtc2v83gptk34reelxyc7wsgnze890c6nrv6p0cmepatc269eayzjjkqk30n52rfl5dg7\
1222 wztl96f7wc2tzx34q909xuajnyt4u4lnk87lwal7z0etdz5tmece0v3u796jfp68nccn05ty54ncfelts3v8g0\
1223 sn6v6hsu87zat4r03368ersu87252dd0nswymxzc2pyxl8yy844hspuyj47w0px4u4leefq568sk0rr9th4ql9\
1224 f9ykawrczkz5hp22nstg3lrlsa6u2q2ull3kzce2sh0h77sjv0zszhzy4hfh6u0pwux5l3gpthsn72mfu47sw9\
1225 zw3hzk7srznp27z0etdp0725me00sn72mgkf0fteehruk0lg6swh34z52puaekzmjlmalhhe6m8ug7z3c8g8zh\
1226 jjspp5zj0sm85g5ufng9w7s6p4ucdk80tyvz64sg54v0cy4vgnr37f78sqsp5l6azu2hv6we30er90jrslqpvd\
1227 trnrphhesca2wg5q83k52rsu2cq9q2sqqqqqysgqcqr8h2np4qw0ha2k282hm8jh5rcfq0hsp2zhddtlc5vs23\
1228 uphyv0lv3k8sqsfgfp4qyrk86tx5xg2aa7et4cdzhnvl5s4nd33ugytt7gamk9tugn9yransr9yq08gpwsn8t2\
1229 tq4ducjfhrcz707av0ss20urjh8vldrpmehqxa0stkesvuq82txyqzfhej7qccswy7k5wvcppk63c6zpjytfda\
1230 ccadacjtn52lpe6s85rjfqlxzp6frq33xshaz2nr9xjkhd3jj8qg39nmfzvpgmayakqmy9rseakwgcudug7hs4\
1231 5wh430ywh7qhj3khczh8gle4cn93ymgfwa7rrvcw9lywyyz58k4p40a3nu9svthaf0qeg8f2ay4tw9p48p70qm\
1232 ayu3ejl2q8pj9e2l22h7775tl44hs6ke4sdfgcr6aj8wra4r2v9sj6xa5chd5ctpfg8chtrer3kkp0e6af88lk\
1233 rfxcklf2hyslv2hr0xl5lwrm5y5uttxn4ndfz8789znf78nspa3xy68"
1234 );
1235 parse_ok(
1237 "lnbcrt17124979001314909880p1y6lkcwgd76tfnxksfk2atyy4tzw4nyg6jrx3282s2ygvcxyj64gevhxsjk\
1238 2ymhzv3e0p5h5u3kfey92jt9ge44gsfnwycxynm2g3unw3ntt9qh25texe98jcfhxvcxuezxw9tngwrndpy9s4\
1239 p4x9eyze2tfe9rxm68tp5yj5jfduen2nny8prhsm6edegn2stww4n4gwp4vfjkvdthd43524n9fa8h262vwesk\
1240 g66nw3vnyafn29zhsvfeg9mxummtfp35uumzfqmhy3jwgdh55mt5xpvhgmjn25uku5e5g939wmmnvdfygnrdgd\
1241 h56uzcx4a92vfhgdcky3z9gfnrsvp4f4f55j68vak9yufhvdm8x5zrgc6955jvf429zumv89nh2a35wae5yntg\
1242 v985jumpxehyv7t92pjrwufs89yh23f5ddy5s568wgchve3cg9ek5nzewgcrzjz0dftxg3nvf4hngje52ac4zm\
1243 esxpvk6sfef4hkuetvd4vk6n29wftrw5rvg4yy2vjjwyexc5mnvfd8xknndpqkkenx0q642j35298hwve3dyc5\
1244 25jrd3295sm9v9jrqup3wpykg7zd239ns7jgtqu95jz0deaxksjh2fu56n6n2f5x6mm8wa89qjfef385sam2x9\
1245 mxcs20gfpnq460d3axzknnf3e4sw2kvf25wjjxddpyg52dw4vx7nn2w9cyu5t8vfnyxjtpg33kssjp24ch536p\
1246 d938snmtx345x6r4x93kvv2tff855um3tfekxjted4kxys2kve5hvu6g89z4ynmjgfhnw7tv892rymejgvey77\
1247 rcfqe9xjr92d85636fvajxyajndfa92k2nxycx5jtjx4zxsm2y2dyn2up50f5ku3nrfdk4g5npxehkzjjv8y69\
1248 gveev4z56denddaxy7tfwe8xx42zgf6kzmnxxpk826ze2s6xk6jrwearw6ejvd8rsvj2fpg525jtd5pp5j2tlt\
1249 28m4kakjr84w6ce4fd8e7awy6ncyswcyut760rdnem30ptssp5p5u3xgxxtr6aev8y2w9m30wcw3kyn7fgm8wm\
1250 f8qw8wzrqt34zcvq9q2sqqqqqysgqcqypmw9xq8lllllllnp4qt36twam2ca08m3s7vnhre3c0j89589wyw4vd\
1251 k7fln0lryxzkdcrur28qwqq3hnyt84vsasuldd2786eysdf4dyuggwsmvw2atftf7spkmpa9dd3efq5tenpqm2\
1252 v7vcz2a4s0s7jnqpjn0srysnstnw5y5z9taxn0ue37aqgufxcdsj6f8a2m4pm9udppdzc4shsdqzzx0u0rm4xl\
1253 js0dqz3c5zqyvglda7nsqvqfztmlyup7vyuadzav4zyuqwx90ev6nmk53nkhkt0sev9e745wxqtdvrqzgqkaka\
1254 zen7e2qmsdauk665g3llg5qtl79t3xulrhjnducehdn72gpmkjvtth7kh6ejpl9dv0qcsxv2jvzzvg0hzdmk3y\
1255 jsmydqksdk3h78kc63qnr265h8vyeslqexszppfm7y287t3gxvhw0ulg2wp0rsw3tevz03z50kpy77zdz9snxm\
1256 kkwxd76xvj4qvj2f89rrnuvdvzw947ay0kydc077pkec2jet9qwp2tud98s24u65uz07eaxk5jk3e4nggn2caa\
1257 ek2p5pkrc6mm6mxjm2ezpdu8p5jstg6tgvnttgac3ygt5ys04t4udujzlshpl7e4f3ff03xe6v24cp6aq4wa"
1258 );
1259 parse_ok(
1261 "lntb5826417333454665580p1c5rwh5edlhf33hvkj5vav5z3t02a5hxvj3vfv5kuny2f3yzj6zwf9hx3nn2fk\
1262 9gepc2a3ywvj6dax5v3jy2d5nxmp3gaxhycjkv38hx4z4d4vyznrp2p24xa6t2pg4w4rrxfens6tcxdhxvvfhx\
1263 a8xvvpkgat8xnpe2p44juz9g43hyur00989gvfhwd2kj72wfum4g4mgx5m5cs2rg9d9vnn6xe89ydnnvfpyy52\
1264 s2dxx2er4x4xxwstdd5cxwdrjw3nkxnnv2uexxnrxw4t56sjswfn52s2xv4t8xmjtwpn8xm6sfeh4q526dyu8x\
1265 3r9gceyw6fhd934qjttvdk57az5w368zdrhwfjxxu35xcmrsmmpd4g8wwtev4tkzutdd32k56mxveuy6c6v2em\
1266 yv7zkfp39zjpjgd8hx7n4xph5kceswf6xxmnyfcuxca20fp24z7ncvfhyu5jf2exhw36nwf68s7rh2a6yzjf4d\
1267 gukcenfxpchqsjn2pt5x334tf98wsm6dvcrvvfcwapxvk2cdvmk2npcfe68zue3w4f9xc6s2fvrw6nrg3fkskt\
1268 e2ftxyc20ffckcd692964sdzjwdp4yvrfdfm9q72pxp3kwat5f4j9xee5da8rss60w92857tgwych55f5w3n8z\
1269 mzexpy4jwredejrqm6txf3nxm64ffh8x460dp9yjazhw4yx6dm5xerysnn5wa455k3h2d89ss2fd9axwjp3f4r\
1270 9qdmfd4fx6stx2eg9sezrv369w7nvvfvhj4nnwaz5z3ny8qcxcdnvwd64jc2nx9uy2e2gxdrnx6r3w9ykxatxx\
1271 g6kk6rv2ekr2emwx5ehy362d3x82dzvddfxs5rcg4vn27npf564qdtg2anycc6523jnwe3e0p65unrpvccrs5m\
1272 2fuexgmnj23ay5e34v4xk5jnrwpg4xemfwqe5vjjjw9qk76zsd9yrzu6xdpv5v5ntdejxg6jtv3kx65t6gdhrg\
1273 vj3fe34sj2vv3h5kegpp57hjf5kv6clw97y2e063yuz0psrz9a6l49v836dflum00rh8qtn8qsp5gd29qycuze\
1274 08xls8l32zjaaf2uqv78v97lg9ss0c699huw980h2q9q2sqqqqqysgqcqr8ulnp4q26hcfwr7qxz7lwwlr2kjc\
1275 rws7m2u5j36mm0kxa45uxy6zvsqt2zzfppjdkrm2rlgadt9dq3d6jkv4r2cugmf2kamr28qwuleyzzyyly8a6t\
1276 u70eldahx7hzxx5x9gms7vjjr577ps8n4qyds5nern39j0v7czkch2letnt46895jupxgehf208xgxz8d6j8gu\
1277 3h2qqtsk9nr9nuquhkqjxw40h2ucpldrawmktxzxdgtkt9a3p95g98nywved8s8laj2a0c98rq5zzdnzddz6nd\
1278 w0lvr6u0av9m7859844cgz9vpeq05gw79zqae2s7jzeq66wydyueqtp56qc67g7krv6lj5aahxtmq4y208q5qy\
1279 z38cnwl9ma6m5f4nhzqaj0tjxpfrk4nr5arv9d20lvxvddvffhzygmyuvwd959uhdcgcgjejchqt2qncuwpqqk\
1280 5vws7dflw8x6esrfwhz7h3jwmhevf445k76nme926sr8drsdveqg7l7t7lnjvhaludqnwk4l2pmevkjf9pla92\
1281 4p77v76r7x8jzyy7h59hmk0lgzfsk6c8dpj37hssj7jt4q7jzvy8hq25l3pag37axxanjqnq56c47gpgy6frsy\
1282 c0str9w2aahz4h6t7axaka4cwvhwg49r6qgj8kwz2mt6vcje25l9ekvmgq5spqtn"
1283 );
1284 }
1285
1286 #[test]
1288 fn test_serde_long_invoice() {
1289 use crate::TaggedField::*;
1290 use crate::{
1291 Bolt11Invoice, Bolt11InvoiceFeatures, Bolt11InvoiceSignature, Currency,
1292 PositiveTimestamp, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, Sha256,
1293 SignedRawBolt11Invoice,
1294 };
1295 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1296 use bitcoin::secp256k1::PublicKey;
1297 use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
1298
1299 fn unknown_semantics_field(len: usize) -> Vec<Fe32> {
1301 assert!(len <= 1023);
1302 let mut field = Vec::with_capacity(len + 3);
1303 field.push(Fe32::Q);
1305 field.push(Fe32::try_from((len >> 5) as u8).unwrap());
1306 field.push(Fe32::try_from((len & 0x1f) as u8).unwrap());
1307 field.extend(std::iter::repeat(Fe32::P).take(len));
1309 field
1310 }
1311
1312 let payment_hash = sha256::Hash::from_str(
1314 "0001020304050607080900010203040506070809000102030405060708090102",
1315 )
1316 .unwrap();
1317 let description = "A".repeat(639);
1318 let fallback_addr = crate::Fallback::SegWitProgram {
1319 version: bitcoin::WitnessVersion::V0,
1320 program: vec![0; 32],
1321 };
1322 let payee_pk = PublicKey::from_slice(&[
1323 0x03, 0x24, 0x65, 0x3e, 0xac, 0x43, 0x44, 0x88, 0x00, 0x2c, 0xc0, 0x6b, 0xbf, 0xb7,
1324 0xf1, 0x0f, 0xe1, 0x89, 0x91, 0xe3, 0x5f, 0x9f, 0xe4, 0x30, 0x2d, 0xbe, 0xa6, 0xd2,
1325 0x35, 0x3d, 0xc0, 0xab, 0x1c,
1326 ])
1327 .unwrap();
1328 let route_hints = std::iter::repeat(RouteHintHop {
1329 src_node_id: payee_pk,
1330 short_channel_id: 0x0102030405060708,
1331 fees: RoutingFees { base_msat: 1, proportional_millionths: 20 },
1332 cltv_expiry_delta: 3,
1333 htlc_minimum_msat: None,
1334 htlc_maximum_msat: None,
1335 })
1336 .take(12)
1337 .collect::<Vec<_>>();
1338
1339 let raw_invoice = RawBolt11Invoice {
1341 hrp: RawHrp {
1342 currency: Currency::Bitcoin,
1343 raw_amount: Some(10000000000000000010),
1344 si_prefix: Some(crate::SiPrefix::Pico),
1345 },
1346 data: RawDataPart {
1347 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1348 tagged_fields: vec![
1349 PaymentHash(Sha256(payment_hash)).into(),
1350 Description(crate::Description::new(description).unwrap()).into(),
1351 PayeePubKey(crate::PayeePubKey(payee_pk)).into(),
1352 ExpiryTime(crate::ExpiryTime(std::time::Duration::from_secs(u64::MAX))).into(),
1353 MinFinalCltvExpiryDelta(crate::MinFinalCltvExpiryDelta(u64::MAX)).into(),
1354 Fallback(fallback_addr).into(),
1355 PrivateRoute(crate::PrivateRoute(RouteHint(route_hints))).into(),
1356 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1357 PaymentMetadata(vec![0x69; 639]).into(),
1358 Features(Bolt11InvoiceFeatures::from_le_bytes(vec![0xaa; 639])).into(),
1359 RawTaggedField::UnknownSemantics(unknown_semantics_field(1023)),
1362 RawTaggedField::UnknownSemantics(unknown_semantics_field(1023)),
1363 RawTaggedField::UnknownSemantics(unknown_semantics_field(576)),
1364 ],
1365 },
1366 };
1367
1368 let hash = [
1370 0x75, 0x99, 0xe1, 0x51, 0x7f, 0xa1, 0x0e, 0xb5, 0xc0, 0x79, 0xb4, 0x6e, 0x8e, 0x62,
1371 0x0c, 0x4f, 0xb0, 0x72, 0x71, 0xd2, 0x81, 0xa1, 0x92, 0x65, 0x9c, 0x90, 0x89, 0x69,
1372 0xe1, 0xf3, 0xd6, 0x59,
1373 ];
1374 let signature = &[
1375 0x6c, 0xbe, 0xbe, 0xfe, 0xd3, 0xfb, 0x07, 0x68, 0xb5, 0x79, 0x98, 0x82, 0x29, 0xab,
1376 0x0e, 0xcc, 0x8d, 0x3a, 0x81, 0xee, 0xee, 0x07, 0xb3, 0x5d, 0x64, 0xca, 0xb4, 0x12,
1377 0x33, 0x99, 0x33, 0x2a, 0x31, 0xc2, 0x2c, 0x2b, 0x62, 0x96, 0x4e, 0x37, 0xd7, 0x96,
1378 0x50, 0x5e, 0xdb, 0xe9, 0xa9, 0x5b, 0x0b, 0x3b, 0x87, 0x22, 0x89, 0xed, 0x95, 0xf1,
1379 0xf1, 0xdf, 0x2d, 0xb6, 0xbd, 0xf5, 0x0a, 0x20,
1380 ];
1381 let signature = Bolt11InvoiceSignature(
1382 RecoverableSignature::from_compact(signature, RecoveryId::from_i32(1).unwrap())
1383 .unwrap(),
1384 );
1385 let signed_invoice = SignedRawBolt11Invoice { raw_invoice, hash, signature };
1386
1387 let invoice = Bolt11Invoice::from_signed(signed_invoice).unwrap();
1389 let invoice_str = invoice.to_string();
1390 assert_eq!(invoice_str.len(), crate::MAX_LENGTH);
1391 assert_eq!(invoice, Bolt11Invoice::from_str(&invoice_str).unwrap());
1392 }
1393
1394 #[test]
1396 fn test_deser_too_long_fails() {
1397 use crate::{Bolt11Invoice, ParseOrSemanticError, MAX_LENGTH};
1398 use bech32::primitives::decode::{CheckedHrpstringError, ChecksumError};
1399
1400 fn parse_is_code_length_err(s: &str) -> bool {
1401 matches!(
1403 Bolt11Invoice::from_str(s),
1404 Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(
1405 CheckedHrpstringError::Checksum(ChecksumError::CodeLength(_))
1406 ))),
1407 )
1408 }
1409
1410 let mut too_long = String::from("lnbc1");
1411 too_long.push_str(
1412 String::from_utf8(vec![b'x'; (MAX_LENGTH + 1) - too_long.len()]).unwrap().as_str(),
1413 );
1414 assert!(parse_is_code_length_err(&too_long));
1415 assert!(!parse_is_code_length_err(&too_long[..too_long.len() - 1]));
1416 }
1417}