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::InvalidRecoveryId => {
776 f.write_str("recovery id is out of range (should be in [0,3])")
777 },
778 Bolt11ParseError::Skip => f.write_str(
779 "the tagged field has to be skipped because of an unexpected, but allowed property",
780 ),
781 }
782 }
783}
784
785impl Display for ParseOrSemanticError {
786 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
787 match self {
788 ParseOrSemanticError::ParseError(err) => err.fmt(f),
789 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
790 }
791 }
792}
793
794#[cfg(feature = "std")]
795impl error::Error for Bolt11ParseError {}
796
797#[cfg(feature = "std")]
798impl error::Error for ParseOrSemanticError {}
799
800macro_rules! from_error {
801 ($my_error:expr, $extern_error:ty) => {
802 impl From<$extern_error> for Bolt11ParseError {
803 fn from(e: $extern_error) -> Self {
804 $my_error(e)
805 }
806 }
807 };
808}
809
810from_error!(Bolt11ParseError::MalformedSignature, bitcoin::secp256k1::Error);
811from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
812from_error!(Bolt11ParseError::DescriptionDecodeError, string::FromUtf8Error);
813
814impl From<CheckedHrpstringError> for Bolt11ParseError {
815 fn from(e: CheckedHrpstringError) -> Self {
816 Self::Bech32Error(e)
817 }
818}
819
820impl From<Bolt11ParseError> for ParseOrSemanticError {
821 fn from(e: Bolt11ParseError) -> Self {
822 ParseOrSemanticError::ParseError(e)
823 }
824}
825
826impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
827 fn from(e: Bolt11SemanticError) -> Self {
828 ParseOrSemanticError::SemanticError(e)
829 }
830}
831
832#[cfg(test)]
833mod test {
834 use super::FromBase32;
835 use crate::de::Bolt11ParseError;
836 use bech32::Fe32;
837 use bitcoin::hashes::sha256;
838 use bitcoin::secp256k1::PublicKey;
839 use std::str::FromStr;
840
841 const CHARSET_REV: [i8; 128] = [
842 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
843 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
844 -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13,
845 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,
846 -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28,
847 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
848 ];
849
850 fn from_bech32(bytes_5b: &[u8]) -> Vec<Fe32> {
851 bytes_5b.iter().map(|c| Fe32::try_from(CHARSET_REV[*c as usize] as u8).unwrap()).collect()
852 }
853
854 #[test]
855 fn test_parse_currency_prefix() {
856 use crate::Currency;
857
858 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
859 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
860 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
861 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
862 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
863 assert_eq!("something_else".parse::<Currency>(), Err(Bolt11ParseError::UnknownCurrency))
864 }
865
866 #[test]
867 fn test_parse_int_from_bytes_be() {
868 use crate::de::parse_u16_be;
869
870 assert_eq!(
871 parse_u16_be(&[
872 Fe32::try_from(1).unwrap(),
873 Fe32::try_from(2).unwrap(),
874 Fe32::try_from(3).unwrap(),
875 Fe32::try_from(4).unwrap(),
876 ]),
877 Some(34916)
878 );
879 assert_eq!(
880 parse_u16_be(&[
881 Fe32::try_from(2).unwrap(),
882 Fe32::try_from(0).unwrap(),
883 Fe32::try_from(0).unwrap(),
884 Fe32::try_from(0).unwrap(),
885 ]),
886 None
887 );
888 }
889
890 #[test]
891 fn test_parse_sha256_hash() {
892 use crate::Sha256;
893
894 let input = from_bech32("qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes());
895
896 let hash = sha256::Hash::from_str(
897 "0001020304050607080900010203040506070809000102030405060708090102",
898 )
899 .unwrap();
900 let expected = Ok(Sha256(hash));
901
902 assert_eq!(Sha256::from_base32(&input), expected);
903
904 let input_unexpected_length =
906 from_bech32("qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes());
907 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
908 }
909
910 #[test]
911 fn test_parse_description() {
912 use crate::Description;
913
914 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
915 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
916 assert_eq!(Description::from_base32(&input), expected);
917 }
918
919 #[test]
920 fn test_parse_payee_pub_key() {
921 use crate::PayeePubKey;
922
923 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
924 let pk_bytes = [
925 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
926 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
927 0x0f, 0x93, 0x4d, 0xd9, 0xad,
928 ];
929 let expected = Ok(PayeePubKey(PublicKey::from_slice(&pk_bytes[..]).unwrap()));
930
931 assert_eq!(PayeePubKey::from_base32(&input), expected);
932
933 let input_unexpected_length =
935 from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes());
936 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
937 }
938
939 #[test]
940 fn test_parse_expiry_time() {
941 use crate::ExpiryTime;
942
943 let input = from_bech32("pu".as_bytes());
944 let expected = Ok(ExpiryTime::from_seconds(60));
945 assert_eq!(ExpiryTime::from_base32(&input), expected);
946
947 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
948 assert_eq!(
949 ExpiryTime::from_base32(&input_too_large),
950 Err(Bolt11ParseError::IntegerOverflowError)
951 );
952 }
953
954 #[test]
955 fn test_parse_min_final_cltv_expiry_delta() {
956 use crate::MinFinalCltvExpiryDelta;
957
958 let input = from_bech32("pr".as_bytes());
959 let expected = Ok(MinFinalCltvExpiryDelta(35));
960
961 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
962 }
963
964 #[test]
965 fn test_parse_fallback() {
966 use crate::Fallback;
967 use bitcoin::hashes::Hash;
968 use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
969
970 let cases = vec![
971 (
972 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
973 Ok(Fallback::PubKeyHash(
974 PubkeyHash::from_slice(&[
975 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59,
976 0xd3, 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7,
977 ])
978 .unwrap(),
979 )),
980 ),
981 (
982 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
983 Ok(Fallback::ScriptHash(
984 ScriptHash::from_slice(&[
985 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9,
986 0xf3, 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45,
987 ])
988 .unwrap(),
989 )),
990 ),
991 (
992 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
993 Ok(Fallback::SegWitProgram {
994 version: WitnessVersion::V0,
995 program: Vec::from(
996 &[
997 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
998 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
999 ][..],
1000 ),
1001 }),
1002 ),
1003 (vec![Fe32::try_from(21).unwrap(); 41], Err(Bolt11ParseError::Skip)),
1004 (vec![], Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)),
1005 (
1006 vec![Fe32::try_from(1).unwrap(); 81],
1007 Err(Bolt11ParseError::InvalidSegWitProgramLength),
1008 ),
1009 (vec![Fe32::try_from(17).unwrap(); 1], Err(Bolt11ParseError::InvalidPubKeyHashLength)),
1010 (vec![Fe32::try_from(18).unwrap(); 1], Err(Bolt11ParseError::InvalidScriptHashLength)),
1011 ];
1012
1013 for (input, expected) in cases.into_iter() {
1014 assert_eq!(Fallback::from_base32(&input), expected);
1015 }
1016 }
1017
1018 #[test]
1019 fn test_parse_route() {
1020 use crate::PrivateRoute;
1021 use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
1022
1023 let input = from_bech32(
1024 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
1025 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
1026 );
1027
1028 let mut expected = Vec::<RouteHintHop>::new();
1029 expected.push(RouteHintHop {
1030 src_node_id: PublicKey::from_slice(
1031 &[
1032 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
1033 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
1034 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55,
1035 ][..],
1036 )
1037 .unwrap(),
1038 short_channel_id: 0x0102030405060708,
1039 fees: RoutingFees { base_msat: 1, proportional_millionths: 20 },
1040 cltv_expiry_delta: 3,
1041 htlc_minimum_msat: None,
1042 htlc_maximum_msat: None,
1043 });
1044 expected.push(RouteHintHop {
1045 src_node_id: PublicKey::from_slice(
1046 &[
1047 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
1048 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
1049 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55,
1050 ][..],
1051 )
1052 .unwrap(),
1053 short_channel_id: 0x030405060708090a,
1054 fees: RoutingFees { base_msat: 2, proportional_millionths: 30 },
1055 cltv_expiry_delta: 4,
1056 htlc_minimum_msat: None,
1057 htlc_maximum_msat: None,
1058 });
1059
1060 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
1061
1062 assert_eq!(
1063 PrivateRoute::from_base32(&[Fe32::try_from(0).unwrap(); 40][..]),
1064 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
1065 );
1066 }
1067
1068 #[test]
1069 fn test_payment_secret_and_features_de_and_ser() {
1070 use crate::TaggedField::*;
1071 use crate::{
1072 Bolt11InvoiceSignature, Currency, PositiveTimestamp, RawBolt11Invoice, RawDataPart,
1073 RawHrp, Sha256, SiPrefix, SignedRawBolt11Invoice,
1074 };
1075 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1076 use lightning_types::features::Bolt11InvoiceFeatures;
1077
1078 let expected_features =
1080 Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
1081 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
1082 let invoice =
1083 SignedRawBolt11Invoice {
1084 raw_invoice: RawBolt11Invoice {
1085 hrp: RawHrp {
1086 currency: Currency::Bitcoin,
1087 raw_amount: Some(25),
1088 si_prefix: Some(SiPrefix::Milli),
1089 },
1090 data: RawDataPart {
1091 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1092 tagged_fields: vec ! [
1093 PaymentHash(Sha256(sha256::Hash::from_str(
1094 "0001020304050607080900010203040506070809000102030405060708090102"
1095 ).unwrap())).into(),
1096 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
1097 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1098 Features(expected_features).into()],
1099 },
1100 },
1101 hash: [
1102 0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1103 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1104 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9,
1105 ],
1106 signature: Bolt11InvoiceSignature(
1107 RecoverableSignature::from_compact(
1108 &[
1109 0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf,
1110 0x68, 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64,
1111 0xd3, 0x60, 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab,
1112 0x4c, 0x85, 0xd3, 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08,
1113 0x12, 0xf9, 0x5d, 0x97, 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda,
1114 0xe0, 0x1a, 0xf3, 0xc1,
1115 ],
1116 RecoveryId::from_i32(1).unwrap(),
1117 )
1118 .unwrap(),
1119 ),
1120 };
1121 assert_eq!(invoice_str, invoice.to_string());
1122 assert_eq!(invoice_str.parse(), Ok(invoice));
1123 }
1124
1125 #[test]
1126 fn test_raw_signed_invoice_deserialization() {
1127 use crate::TaggedField::*;
1128 use crate::{
1129 Bolt11InvoiceSignature, Currency, PositiveTimestamp, RawBolt11Invoice, RawDataPart,
1130 RawHrp, Sha256, SignedRawBolt11Invoice,
1131 };
1132 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1133
1134 assert_eq!(
1135 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1136 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1137 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1138 Ok(SignedRawBolt11Invoice {
1139 raw_invoice: RawBolt11Invoice {
1140 hrp: RawHrp {
1141 currency: Currency::Bitcoin,
1142 raw_amount: None,
1143 si_prefix: None,
1144 },
1145 data: RawDataPart {
1146 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1147 tagged_fields: vec ! [
1148 PaymentHash(Sha256(sha256::Hash::from_str(
1149 "0001020304050607080900010203040506070809000102030405060708090102"
1150 ).unwrap())).into(),
1151 Description(
1152 crate::Description::new(
1153 "Please consider supporting this project".to_owned()
1154 ).unwrap()
1155 ).into(),
1156 ],
1157 },
1158 },
1159 hash: [
1160 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1161 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1162 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1163 ],
1164 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1165 & [
1166 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1167 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1168 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1169 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1170 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1171 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1172 ],
1173 RecoveryId::from_i32(0).unwrap()
1174 ).unwrap()),
1175 }
1176 )
1177 )
1178 }
1179
1180 #[test]
1183 fn test_deser_long_test_vectors() {
1184 use crate::Bolt11Invoice;
1185
1186 #[track_caller]
1187 fn parse_ok(invoice_str: &str) {
1188 let invoice = Bolt11Invoice::from_str(invoice_str).unwrap();
1189 let invoice_str2 = invoice.to_string();
1190 if invoice_str != invoice_str2 {
1191 panic!(
1192 "Invoice does not roundtrip: invoice_str != invoice_str2\n\
1193 invoice_str: {invoice_str}\n\
1194 invoice_str2: {invoice_str2}\n\
1195 \n\
1196 {invoice:?}"
1197 );
1198 }
1199 }
1200
1201 parse_ok(
1203 "lnbc10000000000000000010p1qqqqqqqdtuxpqkzq8sjzqgps4pvyczqq8sjzqgpuysszq0pyyqsrp2zs0sjz\
1204 qgps4pxrcfpqyqc2slpyyqsqsv9gwz59s5zqpqyps5rc9qsrs2pqxz5ysyzcfqgysyzs0sjzqgqq8sjzqgps4p\
1205 xqqzps4pqpssqgzpxps5ruysszqrps4pg8p2zgpsc2snpuysszqzqsgqvys0pyyqsrcfpqyqvycv9gfqqrcfpq\
1206 yq7zggpq8q5zqyruysszqwpgyqxpsjqsgq7zggpqps7zggpq8sjzqgqgqq7zggpqpq7zggpq8q5zqqpuysszq0\
1207 pyyqsqs0pyyqspsnqgzpqpqlpyyqsqszpuysszqyzvzpvysrqq8sjzqgqvrp7zggpqpqxpsspp5mf45hs3cgph\
1208 h0074r5qmr74y82r26ac4pzdg4nd9mdmsvz6ffqpssp5vr4yra4pcv74h9hk3d0233nqu4gktpuykjamrafrdp\
1209 uedqugzh3q9q2sqqqqqysgqcqrpqqxq8pqqqqqqnp4qgvcxpme2q5lng36j9gruwlrtk2f86s3c5xmk87yhvyu\
1210 wdeh025q5r9yqwnqegv9hj9nzkhyxaeyq92wcrnqp36pyrc2qzrvswj5g96ey2dn6qqqqqqqqqqqqqqqqqqqqq\
1211 qqqqqqqqp9a5vs0t4z56p64xyma8s84yvdx7uhqj0gvrr424fea2wpztq2fwqqqqqqqqqqqqqqqqqqqqqqqqqq\
1212 qqqqmy9qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
1213 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpcnsxc32du9n7amlypuhclzqrt6lkegq\
1214 0v3r7nczjv9tv30z7phq80r3dm7pvgykl7gwuenmem93h5xwdwac6ngsmzqc34khrg3qjgsq6qk6lc"
1215 );
1216 parse_ok(
1218 "lnbc8735500635020489010p1av5kfs8deupvyk4u5ynj03hmalhhhml0fxc2jlrv9z4lg6s4hnhkz69malhhe\
1219 t3x9yqpsxru4a3kwar2qtu2q2ughx367q600s5x7c7tln4k0fu78skxqevaqm8sayhuur377zgf3uf94n57xzh\
1220 dw99u42hwc089djn5xj723w7zageflsnzdmyte89tecf2ac7xhg4y3u9f4xpuv2hwxjlsarp0e24fu8tme6rgv\
1221 0tqj08z9f4u30rw59k8emhtvs7wye0xfw6x5q5tju2p208rvtkunzwtwghtp22tlnh62gxwhfkxp4cnz7ts3rx\
1222 vlzszhv9y00h77lpdvcjyhjtmalh5dn5e8n5w8cqle0vunzduu4nza9y0734qhxday9hzywl0aa0vhzy0qmphc\
1223 64d4hduj08dv2krpgqtc2v83gptk34reelxyc7wsgnze890c6nrv6p0cmepatc269eayzjjkqk30n52rfl5dg7\
1224 wztl96f7wc2tzx34q909xuajnyt4u4lnk87lwal7z0etdz5tmece0v3u796jfp68nccn05ty54ncfelts3v8g0\
1225 sn6v6hsu87zat4r03368ersu87252dd0nswymxzc2pyxl8yy844hspuyj47w0px4u4leefq568sk0rr9th4ql9\
1226 f9ykawrczkz5hp22nstg3lrlsa6u2q2ull3kzce2sh0h77sjv0zszhzy4hfh6u0pwux5l3gpthsn72mfu47sw9\
1227 zw3hzk7srznp27z0etdp0725me00sn72mgkf0fteehruk0lg6swh34z52puaekzmjlmalhhe6m8ug7z3c8g8zh\
1228 jjspp5zj0sm85g5ufng9w7s6p4ucdk80tyvz64sg54v0cy4vgnr37f78sqsp5l6azu2hv6we30er90jrslqpvd\
1229 trnrphhesca2wg5q83k52rsu2cq9q2sqqqqqysgqcqr8h2np4qw0ha2k282hm8jh5rcfq0hsp2zhddtlc5vs23\
1230 uphyv0lv3k8sqsfgfp4qyrk86tx5xg2aa7et4cdzhnvl5s4nd33ugytt7gamk9tugn9yransr9yq08gpwsn8t2\
1231 tq4ducjfhrcz707av0ss20urjh8vldrpmehqxa0stkesvuq82txyqzfhej7qccswy7k5wvcppk63c6zpjytfda\
1232 ccadacjtn52lpe6s85rjfqlxzp6frq33xshaz2nr9xjkhd3jj8qg39nmfzvpgmayakqmy9rseakwgcudug7hs4\
1233 5wh430ywh7qhj3khczh8gle4cn93ymgfwa7rrvcw9lywyyz58k4p40a3nu9svthaf0qeg8f2ay4tw9p48p70qm\
1234 ayu3ejl2q8pj9e2l22h7775tl44hs6ke4sdfgcr6aj8wra4r2v9sj6xa5chd5ctpfg8chtrer3kkp0e6af88lk\
1235 rfxcklf2hyslv2hr0xl5lwrm5y5uttxn4ndfz8789znf78nspa3xy68"
1236 );
1237 parse_ok(
1239 "lnbcrt17124979001314909880p1y6lkcwgd76tfnxksfk2atyy4tzw4nyg6jrx3282s2ygvcxyj64gevhxsjk\
1240 2ymhzv3e0p5h5u3kfey92jt9ge44gsfnwycxynm2g3unw3ntt9qh25texe98jcfhxvcxuezxw9tngwrndpy9s4\
1241 p4x9eyze2tfe9rxm68tp5yj5jfduen2nny8prhsm6edegn2stww4n4gwp4vfjkvdthd43524n9fa8h262vwesk\
1242 g66nw3vnyafn29zhsvfeg9mxummtfp35uumzfqmhy3jwgdh55mt5xpvhgmjn25uku5e5g939wmmnvdfygnrdgd\
1243 h56uzcx4a92vfhgdcky3z9gfnrsvp4f4f55j68vak9yufhvdm8x5zrgc6955jvf429zumv89nh2a35wae5yntg\
1244 v985jumpxehyv7t92pjrwufs89yh23f5ddy5s568wgchve3cg9ek5nzewgcrzjz0dftxg3nvf4hngje52ac4zm\
1245 esxpvk6sfef4hkuetvd4vk6n29wftrw5rvg4yy2vjjwyexc5mnvfd8xknndpqkkenx0q642j35298hwve3dyc5\
1246 25jrd3295sm9v9jrqup3wpykg7zd239ns7jgtqu95jz0deaxksjh2fu56n6n2f5x6mm8wa89qjfef385sam2x9\
1247 mxcs20gfpnq460d3axzknnf3e4sw2kvf25wjjxddpyg52dw4vx7nn2w9cyu5t8vfnyxjtpg33kssjp24ch536p\
1248 d938snmtx345x6r4x93kvv2tff855um3tfekxjted4kxys2kve5hvu6g89z4ynmjgfhnw7tv892rymejgvey77\
1249 rcfqe9xjr92d85636fvajxyajndfa92k2nxycx5jtjx4zxsm2y2dyn2up50f5ku3nrfdk4g5npxehkzjjv8y69\
1250 gveev4z56denddaxy7tfwe8xx42zgf6kzmnxxpk826ze2s6xk6jrwearw6ejvd8rsvj2fpg525jtd5pp5j2tlt\
1251 28m4kakjr84w6ce4fd8e7awy6ncyswcyut760rdnem30ptssp5p5u3xgxxtr6aev8y2w9m30wcw3kyn7fgm8wm\
1252 f8qw8wzrqt34zcvq9q2sqqqqqysgqcqypmw9xq8lllllllnp4qt36twam2ca08m3s7vnhre3c0j89589wyw4vd\
1253 k7fln0lryxzkdcrur28qwqq3hnyt84vsasuldd2786eysdf4dyuggwsmvw2atftf7spkmpa9dd3efq5tenpqm2\
1254 v7vcz2a4s0s7jnqpjn0srysnstnw5y5z9taxn0ue37aqgufxcdsj6f8a2m4pm9udppdzc4shsdqzzx0u0rm4xl\
1255 js0dqz3c5zqyvglda7nsqvqfztmlyup7vyuadzav4zyuqwx90ev6nmk53nkhkt0sev9e745wxqtdvrqzgqkaka\
1256 zen7e2qmsdauk665g3llg5qtl79t3xulrhjnducehdn72gpmkjvtth7kh6ejpl9dv0qcsxv2jvzzvg0hzdmk3y\
1257 jsmydqksdk3h78kc63qnr265h8vyeslqexszppfm7y287t3gxvhw0ulg2wp0rsw3tevz03z50kpy77zdz9snxm\
1258 kkwxd76xvj4qvj2f89rrnuvdvzw947ay0kydc077pkec2jet9qwp2tud98s24u65uz07eaxk5jk3e4nggn2caa\
1259 ek2p5pkrc6mm6mxjm2ezpdu8p5jstg6tgvnttgac3ygt5ys04t4udujzlshpl7e4f3ff03xe6v24cp6aq4wa"
1260 );
1261 parse_ok(
1263 "lntb5826417333454665580p1c5rwh5edlhf33hvkj5vav5z3t02a5hxvj3vfv5kuny2f3yzj6zwf9hx3nn2fk\
1264 9gepc2a3ywvj6dax5v3jy2d5nxmp3gaxhycjkv38hx4z4d4vyznrp2p24xa6t2pg4w4rrxfens6tcxdhxvvfhx\
1265 a8xvvpkgat8xnpe2p44juz9g43hyur00989gvfhwd2kj72wfum4g4mgx5m5cs2rg9d9vnn6xe89ydnnvfpyy52\
1266 s2dxx2er4x4xxwstdd5cxwdrjw3nkxnnv2uexxnrxw4t56sjswfn52s2xv4t8xmjtwpn8xm6sfeh4q526dyu8x\
1267 3r9gceyw6fhd934qjttvdk57az5w368zdrhwfjxxu35xcmrsmmpd4g8wwtev4tkzutdd32k56mxveuy6c6v2em\
1268 yv7zkfp39zjpjgd8hx7n4xph5kceswf6xxmnyfcuxca20fp24z7ncvfhyu5jf2exhw36nwf68s7rh2a6yzjf4d\
1269 gukcenfxpchqsjn2pt5x334tf98wsm6dvcrvvfcwapxvk2cdvmk2npcfe68zue3w4f9xc6s2fvrw6nrg3fkskt\
1270 e2ftxyc20ffckcd692964sdzjwdp4yvrfdfm9q72pxp3kwat5f4j9xee5da8rss60w92857tgwych55f5w3n8z\
1271 mzexpy4jwredejrqm6txf3nxm64ffh8x460dp9yjazhw4yx6dm5xerysnn5wa455k3h2d89ss2fd9axwjp3f4r\
1272 9qdmfd4fx6stx2eg9sezrv369w7nvvfvhj4nnwaz5z3ny8qcxcdnvwd64jc2nx9uy2e2gxdrnx6r3w9ykxatxx\
1273 g6kk6rv2ekr2emwx5ehy362d3x82dzvddfxs5rcg4vn27npf564qdtg2anycc6523jnwe3e0p65unrpvccrs5m\
1274 2fuexgmnj23ay5e34v4xk5jnrwpg4xemfwqe5vjjjw9qk76zsd9yrzu6xdpv5v5ntdejxg6jtv3kx65t6gdhrg\
1275 vj3fe34sj2vv3h5kegpp57hjf5kv6clw97y2e063yuz0psrz9a6l49v836dflum00rh8qtn8qsp5gd29qycuze\
1276 08xls8l32zjaaf2uqv78v97lg9ss0c699huw980h2q9q2sqqqqqysgqcqr8ulnp4q26hcfwr7qxz7lwwlr2kjc\
1277 rws7m2u5j36mm0kxa45uxy6zvsqt2zzfppjdkrm2rlgadt9dq3d6jkv4r2cugmf2kamr28qwuleyzzyyly8a6t\
1278 u70eldahx7hzxx5x9gms7vjjr577ps8n4qyds5nern39j0v7czkch2letnt46895jupxgehf208xgxz8d6j8gu\
1279 3h2qqtsk9nr9nuquhkqjxw40h2ucpldrawmktxzxdgtkt9a3p95g98nywved8s8laj2a0c98rq5zzdnzddz6nd\
1280 w0lvr6u0av9m7859844cgz9vpeq05gw79zqae2s7jzeq66wydyueqtp56qc67g7krv6lj5aahxtmq4y208q5qy\
1281 z38cnwl9ma6m5f4nhzqaj0tjxpfrk4nr5arv9d20lvxvddvffhzygmyuvwd959uhdcgcgjejchqt2qncuwpqqk\
1282 5vws7dflw8x6esrfwhz7h3jwmhevf445k76nme926sr8drsdveqg7l7t7lnjvhaludqnwk4l2pmevkjf9pla92\
1283 4p77v76r7x8jzyy7h59hmk0lgzfsk6c8dpj37hssj7jt4q7jzvy8hq25l3pag37axxanjqnq56c47gpgy6frsy\
1284 c0str9w2aahz4h6t7axaka4cwvhwg49r6qgj8kwz2mt6vcje25l9ekvmgq5spqtn"
1285 );
1286 }
1287
1288 #[test]
1290 fn test_serde_long_invoice() {
1291 use crate::TaggedField::*;
1292 use crate::{
1293 Bolt11Invoice, Bolt11InvoiceFeatures, Bolt11InvoiceSignature, Currency,
1294 PositiveTimestamp, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, Sha256,
1295 SignedRawBolt11Invoice,
1296 };
1297 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1298 use bitcoin::secp256k1::PublicKey;
1299 use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
1300
1301 fn unknown_semantics_field(len: usize) -> Vec<Fe32> {
1303 assert!(len <= 1023);
1304 let mut field = Vec::with_capacity(len + 3);
1305 field.push(Fe32::Q);
1307 field.push(Fe32::try_from((len >> 5) as u8).unwrap());
1308 field.push(Fe32::try_from((len & 0x1f) as u8).unwrap());
1309 field.extend(std::iter::repeat(Fe32::P).take(len));
1311 field
1312 }
1313
1314 let payment_hash = sha256::Hash::from_str(
1316 "0001020304050607080900010203040506070809000102030405060708090102",
1317 )
1318 .unwrap();
1319 let description = std::iter::repeat("A").take(639).collect::<String>();
1320 let fallback_addr = crate::Fallback::SegWitProgram {
1321 version: bitcoin::WitnessVersion::V0,
1322 program: vec![0; 32],
1323 };
1324 let payee_pk = PublicKey::from_slice(&[
1325 0x03, 0x24, 0x65, 0x3e, 0xac, 0x43, 0x44, 0x88, 0x00, 0x2c, 0xc0, 0x6b, 0xbf, 0xb7,
1326 0xf1, 0x0f, 0xe1, 0x89, 0x91, 0xe3, 0x5f, 0x9f, 0xe4, 0x30, 0x2d, 0xbe, 0xa6, 0xd2,
1327 0x35, 0x3d, 0xc0, 0xab, 0x1c,
1328 ])
1329 .unwrap();
1330 let route_hints = std::iter::repeat(RouteHintHop {
1331 src_node_id: payee_pk,
1332 short_channel_id: 0x0102030405060708,
1333 fees: RoutingFees { base_msat: 1, proportional_millionths: 20 },
1334 cltv_expiry_delta: 3,
1335 htlc_minimum_msat: None,
1336 htlc_maximum_msat: None,
1337 })
1338 .take(12)
1339 .collect::<Vec<_>>();
1340
1341 let raw_invoice = RawBolt11Invoice {
1343 hrp: RawHrp {
1344 currency: Currency::Bitcoin,
1345 raw_amount: Some(10000000000000000010),
1346 si_prefix: Some(crate::SiPrefix::Pico),
1347 },
1348 data: RawDataPart {
1349 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1350 tagged_fields: vec![
1351 PaymentHash(Sha256(payment_hash)).into(),
1352 Description(crate::Description::new(description).unwrap()).into(),
1353 PayeePubKey(crate::PayeePubKey(payee_pk)).into(),
1354 ExpiryTime(crate::ExpiryTime(std::time::Duration::from_secs(u64::MAX))).into(),
1355 MinFinalCltvExpiryDelta(crate::MinFinalCltvExpiryDelta(u64::MAX)).into(),
1356 Fallback(fallback_addr).into(),
1357 PrivateRoute(crate::PrivateRoute(RouteHint(route_hints))).into(),
1358 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1359 PaymentMetadata(vec![0x69; 639]).into(),
1360 Features(Bolt11InvoiceFeatures::from_le_bytes(vec![0xaa; 639])).into(),
1361 RawTaggedField::UnknownSemantics(unknown_semantics_field(1023)),
1364 RawTaggedField::UnknownSemantics(unknown_semantics_field(1023)),
1365 RawTaggedField::UnknownSemantics(unknown_semantics_field(576)),
1366 ],
1367 },
1368 };
1369
1370 let hash = [
1372 0x75, 0x99, 0xe1, 0x51, 0x7f, 0xa1, 0x0e, 0xb5, 0xc0, 0x79, 0xb4, 0x6e, 0x8e, 0x62,
1373 0x0c, 0x4f, 0xb0, 0x72, 0x71, 0xd2, 0x81, 0xa1, 0x92, 0x65, 0x9c, 0x90, 0x89, 0x69,
1374 0xe1, 0xf3, 0xd6, 0x59,
1375 ];
1376 let signature = &[
1377 0x6c, 0xbe, 0xbe, 0xfe, 0xd3, 0xfb, 0x07, 0x68, 0xb5, 0x79, 0x98, 0x82, 0x29, 0xab,
1378 0x0e, 0xcc, 0x8d, 0x3a, 0x81, 0xee, 0xee, 0x07, 0xb3, 0x5d, 0x64, 0xca, 0xb4, 0x12,
1379 0x33, 0x99, 0x33, 0x2a, 0x31, 0xc2, 0x2c, 0x2b, 0x62, 0x96, 0x4e, 0x37, 0xd7, 0x96,
1380 0x50, 0x5e, 0xdb, 0xe9, 0xa9, 0x5b, 0x0b, 0x3b, 0x87, 0x22, 0x89, 0xed, 0x95, 0xf1,
1381 0xf1, 0xdf, 0x2d, 0xb6, 0xbd, 0xf5, 0x0a, 0x20,
1382 ];
1383 let signature = Bolt11InvoiceSignature(
1384 RecoverableSignature::from_compact(signature, RecoveryId::from_i32(1).unwrap())
1385 .unwrap(),
1386 );
1387 let signed_invoice = SignedRawBolt11Invoice { raw_invoice, hash, signature };
1388
1389 let invoice = Bolt11Invoice::from_signed(signed_invoice).unwrap();
1391 let invoice_str = invoice.to_string();
1392 assert_eq!(invoice_str.len(), crate::MAX_LENGTH);
1393 assert_eq!(invoice, Bolt11Invoice::from_str(&invoice_str).unwrap());
1394 }
1395
1396 #[test]
1398 fn test_deser_too_long_fails() {
1399 use crate::{Bolt11Invoice, ParseOrSemanticError, MAX_LENGTH};
1400 use bech32::primitives::decode::{CheckedHrpstringError, ChecksumError};
1401
1402 fn parse_is_code_length_err(s: &str) -> bool {
1403 matches!(
1405 Bolt11Invoice::from_str(s),
1406 Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(
1407 CheckedHrpstringError::Checksum(ChecksumError::CodeLength(_))
1408 ))),
1409 )
1410 }
1411
1412 let mut too_long = String::from("lnbc1");
1413 too_long.push_str(
1414 String::from_utf8(vec![b'x'; (MAX_LENGTH + 1) - too_long.len()]).unwrap().as_str(),
1415 );
1416 assert!(parse_is_code_length_err(&too_long));
1417 assert!(!parse_is_code_length_err(&too_long[..too_long.len() - 1]));
1418 }
1419}