lightning_signer/util/
invoice_utils.rs1use crate::invoice::*;
2use crate::prelude::*;
3use crate::util::status::{invalid_argument, Status};
4use bitcoin::bech32::primitives::decode::UncheckedHrpstring;
5use bitcoin::bech32::{Fe32, Fe32IterExt};
6use core::str::FromStr;
7use lightning::ln::msgs::DecodeError;
8use lightning::offers::parse::Bolt12ParseError;
9
10const BECH32_HRP: &'static str = "lni";
11
12impl FromStr for Invoice {
13 type Err = Status;
14 fn from_str(invstr: &str) -> Result<Self, Self::Err> {
15 let maybe_bolt12 = WrappedBolt12Invoice::from_str(invstr);
17 match maybe_bolt12 {
18 Ok(bolt12) => Ok(Invoice::Bolt12(bolt12.0)),
19 Err(bolt12err) => {
20 let bolt11_raw = invstr.parse::<bolt11::SignedRawBolt11Invoice>().map_err(|e| {
21 Status::invalid_argument(format!(
22 "invoice not bolt12: {:?} and not bolt11: {:?}",
23 bolt12err, e
24 ))
25 })?;
26 Ok(Invoice::try_from(bolt11_raw)?)
27 }
28 }
29 }
30}
31
32impl TryFrom<bolt11::SignedRawBolt11Invoice> for Invoice {
34 type Error = Status;
35 fn try_from(bolt11_raw: bolt11::SignedRawBolt11Invoice) -> Result<Self, Self::Error> {
36 let bolt11 = bolt11::Bolt11Invoice::from_signed(bolt11_raw)
38 .map_err(|e| invalid_argument(e.to_string()))?;
39 Ok(Invoice::Bolt11(bolt11))
40 }
41}
42
43struct WrappedBolt12Invoice(bolt12::Bolt12Invoice);
47
48impl FromStr for WrappedBolt12Invoice {
49 type Err = Bolt12ParseError;
50
51 fn from_str(s: &str) -> Result<WrappedBolt12Invoice, Bolt12ParseError> {
52 let encoded = match s.split('+').skip(1).next() {
54 Some(_) => {
55 for chunk in s.split('+') {
56 let chunk = chunk.trim_start();
57 if chunk.is_empty() || chunk.contains(char::is_whitespace) {
58 return Err(Bolt12ParseError::InvalidContinuation);
59 }
60 }
61
62 let s: String = s.chars().filter(|c| *c != '+' && !c.is_whitespace()).collect();
63 Bech32String::Owned(s)
64 }
65 None => Bech32String::Borrowed(s),
66 };
67
68 let hrp_string = UncheckedHrpstring::new(encoded.as_ref())
70 .map_err(|_| Bolt12ParseError::Decode(DecodeError::InvalidValue))?;
71
72 if hrp_string.hrp().as_str() != BECH32_HRP {
73 return Err(Bolt12ParseError::InvalidBech32Hrp);
74 }
75
76 let data = hrp_string
77 .data_part_ascii()
78 .into_iter()
79 .map(|c| Fe32::from_char_unchecked(*c))
80 .fes_to_bytes()
81 .collect::<Vec<_>>();
82
83 Ok(WrappedBolt12Invoice(bolt12::Bolt12Invoice::try_from(data)?))
84 }
85}
86
87enum Bech32String<'a> {
89 Borrowed(&'a str),
90 Owned(String),
91}
92
93impl<'a> AsRef<str> for Bech32String<'a> {
94 fn as_ref(&self) -> &str {
95 match self {
96 Bech32String::Borrowed(s) => s,
97 Bech32String::Owned(s) => s,
98 }
99 }
100}