1 #[cfg(feature = "std")]
3 use core::convert::TryFrom;
5 use core::fmt::{Display, Formatter};
6 use core::num::ParseIntError;
8 use core::str::FromStr;
10 use bech32::{u5, FromBase32};
12 use bitcoin::{PubkeyHash, ScriptHash};
13 use bitcoin::address::WitnessVersion;
14 use bitcoin::hashes::Hash;
15 use bitcoin::hashes::sha256;
16 use crate::prelude::*;
17 use lightning::ln::PaymentSecret;
18 use lightning::routing::gossip::RoutingFees;
19 use lightning::routing::router::{RouteHint, RouteHintHop};
21 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
22 use secp256k1::PublicKey;
24 use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
25 Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
26 constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures};
28 use self::hrp_sm::parse_hrp;
30 /// State machine to parse the hrp
34 #[derive(PartialEq, Eq, Debug)]
45 fn next_state(&self, read_symbol: char) -> Result<States, super::Bolt11ParseError> {
48 if read_symbol == 'l' {
51 Err(super::Bolt11ParseError::MalformedHRP)
55 if read_symbol == 'n' {
58 Err(super::Bolt11ParseError::MalformedHRP)
62 if !read_symbol.is_numeric() {
63 Ok(States::ParseCurrencyPrefix)
65 Ok(States::ParseAmountNumber)
68 States::ParseCurrencyPrefix => {
69 if !read_symbol.is_numeric() {
70 Ok(States::ParseCurrencyPrefix)
72 Ok(States::ParseAmountNumber)
75 States::ParseAmountNumber => {
76 if read_symbol.is_numeric() {
77 Ok(States::ParseAmountNumber)
78 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
79 Ok(States::ParseAmountSiPrefix)
81 Err(super::Bolt11ParseError::UnknownSiPrefix)
84 States::ParseAmountSiPrefix => Err(super::Bolt11ParseError::MalformedHRP),
88 fn is_final(&self) -> bool {
89 !(*self == States::ParseL || *self == States::ParseN)
97 currency_prefix: Option<Range<usize>>,
98 amount_number: Option<Range<usize>>,
99 amount_si_prefix: Option<Range<usize>>,
103 fn new() -> StateMachine {
105 state: States::Start,
107 currency_prefix: None,
109 amount_si_prefix: None,
113 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
114 let new_range = match *range {
115 None => Range {start: position, end: position + 1},
116 Some(ref r) => Range {start: r.start, end: r.end + 1},
118 *range = Some(new_range);
121 fn step(&mut self, c: char) -> Result<(), super::Bolt11ParseError> {
122 let next_state = self.state.next_state(c)?;
124 States::ParseCurrencyPrefix => {
125 StateMachine::update_range(&mut self.currency_prefix, self.position)
127 States::ParseAmountNumber => {
128 StateMachine::update_range(&mut self.amount_number, self.position)
130 States::ParseAmountSiPrefix => {
131 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
137 self.state = next_state;
141 fn is_final(&self) -> bool {
142 self.state.is_final()
145 fn currency_prefix(&self) -> &Option<Range<usize>> {
146 &self.currency_prefix
149 fn amount_number(&self) -> &Option<Range<usize>> {
153 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
154 &self.amount_si_prefix
158 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> {
159 let mut sm = StateMachine::new();
160 for c in input.chars() {
165 return Err(super::Bolt11ParseError::MalformedHRP);
168 let currency = sm.currency_prefix().clone()
169 .map(|r| &input[r]).unwrap_or("");
170 let amount = sm.amount_number().clone()
171 .map(|r| &input[r]).unwrap_or("");
172 let si = sm.amount_si_prefix().clone()
173 .map(|r| &input[r]).unwrap_or("");
175 Ok((currency, amount, si))
180 impl FromStr for super::Currency {
181 type Err = Bolt11ParseError;
183 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
184 match currency_prefix {
185 "bc" => Ok(Currency::Bitcoin),
186 "tb" => Ok(Currency::BitcoinTestnet),
187 "bcrt" => Ok(Currency::Regtest),
188 "sb" => Ok(Currency::Simnet),
189 "tbs" => Ok(Currency::Signet),
190 _ => Err(Bolt11ParseError::UnknownCurrency)
195 impl FromStr for SiPrefix {
196 type Err = Bolt11ParseError;
198 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
199 use crate::SiPrefix::*;
200 match currency_prefix {
205 _ => Err(Bolt11ParseError::UnknownSiPrefix)
211 /// use lightning_invoice::Bolt11Invoice;
214 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
215 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
216 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
217 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
218 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
219 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
220 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
221 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
222 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
223 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
224 /// j5r6drg6k6zcqj0fcwg";
226 /// assert!(invoice.parse::<Bolt11Invoice>().is_ok());
228 impl FromStr for Bolt11Invoice {
229 type Err = ParseOrSemanticError;
231 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
232 let signed = s.parse::<SignedRawBolt11Invoice>()?;
233 Ok(Bolt11Invoice::from_signed(signed)?)
238 /// use lightning_invoice::*;
240 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
241 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
242 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
243 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
244 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
245 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
246 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
247 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
248 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
249 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
250 /// j5r6drg6k6zcqj0fcwg";
252 /// let parsed_1 = invoice.parse::<Bolt11Invoice>();
254 /// let parsed_2 = match invoice.parse::<SignedRawBolt11Invoice>() {
255 /// Ok(signed) => match Bolt11Invoice::from_signed(signed) {
256 /// Ok(invoice) => Ok(invoice),
257 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
259 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
262 /// assert!(parsed_1.is_ok());
263 /// assert_eq!(parsed_1, parsed_2);
265 impl FromStr for SignedRawBolt11Invoice {
266 type Err = Bolt11ParseError;
268 fn from_str(s: &str) -> Result<Self, Self::Err> {
269 let (hrp, data, var) = bech32::decode(s)?;
271 if var == bech32::Variant::Bech32m {
272 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
273 // we didn't support Bech32m (which lightning does not use).
274 return Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum));
277 if data.len() < 104 {
278 return Err(Bolt11ParseError::TooShortDataPart);
281 let raw_hrp: RawHrp = hrp.parse()?;
282 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
284 Ok(SignedRawBolt11Invoice {
285 raw_invoice: RawBolt11Invoice {
289 hash: RawBolt11Invoice::hash_from_parts(
291 &data[..data.len()-104]
293 signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
298 impl FromStr for RawHrp {
299 type Err = Bolt11ParseError;
301 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
302 let parts = parse_hrp(hrp)?;
304 let currency = parts.0.parse::<Currency>()?;
306 let amount = if !parts.1.is_empty() {
307 Some(parts.1.parse::<u64>()?)
312 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
315 let si: SiPrefix = parts.2.parse()?;
316 if let Some(amt) = amount {
317 if amt.checked_mul(si.multiplier()).is_none() {
318 return Err(Bolt11ParseError::IntegerOverflowError);
332 impl FromBase32 for RawDataPart {
333 type Err = Bolt11ParseError;
335 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
336 if data.len() < 7 { // timestamp length
337 return Err(Bolt11ParseError::TooShortDataPart);
340 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
341 let tagged = parse_tagged_parts(&data[7..])?;
345 tagged_fields: tagged,
350 impl FromBase32 for PositiveTimestamp {
351 type Err = Bolt11ParseError;
353 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
355 return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
357 let timestamp: u64 = parse_u64_be(b32)
358 .expect("7*5bit < 64bit, no overflow possible");
359 match PositiveTimestamp::from_unix_timestamp(timestamp) {
361 Err(_) => unreachable!(),
366 impl FromBase32 for Bolt11InvoiceSignature {
367 type Err = Bolt11ParseError;
368 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
369 if signature.len() != 104 {
370 return Err(Bolt11ParseError::InvalidSliceLength("Bolt11InvoiceSignature::from_base32()".into()));
372 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
373 let signature = &recoverable_signature_bytes[0..64];
374 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
376 Ok(Bolt11InvoiceSignature(RecoverableSignature::from_compact(
383 macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
384 fn $name(digits: &[u5]) -> Option<$ty> {
385 digits.iter().fold(Some(Default::default()), |acc, b|
387 .and_then(|x| x.checked_mul(32))
388 .and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
392 define_parse_int_be!(parse_u16_be, u16);
393 define_parse_int_be!(parse_u64_be, u64);
395 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
396 let mut parts = Vec::<RawTaggedField>::new();
399 while !data.is_empty() {
401 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
404 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
405 // parse the length to find the end of the tagged field's data
406 let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize;
407 let last_element = 3 + len;
409 if data.len() < last_element {
410 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
413 // Get the tagged field's data slice
414 let field = &data[0..last_element];
416 // Set data slice to remaining data
417 data = &data[last_element..];
419 match TaggedField::from_base32(field) {
421 parts.push(RawTaggedField::KnownSemantics(field))
423 Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
424 parts.push(RawTaggedField::UnknownSemantics(field.into()))
426 Err(e) => {return Err(e)}
432 impl FromBase32 for TaggedField {
433 type Err = Bolt11ParseError;
435 fn from_base32(field: &[u5]) -> Result<TaggedField, Bolt11ParseError> {
437 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
441 let field_data = &field[3..];
444 constants::TAG_PAYMENT_HASH =>
445 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
446 constants::TAG_DESCRIPTION =>
447 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
448 constants::TAG_PAYEE_PUB_KEY =>
449 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
450 constants::TAG_DESCRIPTION_HASH =>
451 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
452 constants::TAG_EXPIRY_TIME =>
453 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
454 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA =>
455 Ok(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta::from_base32(field_data)?)),
456 constants::TAG_FALLBACK =>
457 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
458 constants::TAG_PRIVATE_ROUTE =>
459 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
460 constants::TAG_PAYMENT_SECRET =>
461 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
462 constants::TAG_PAYMENT_METADATA =>
463 Ok(TaggedField::PaymentMetadata(Vec::<u8>::from_base32(field_data)?)),
464 constants::TAG_FEATURES =>
465 Ok(TaggedField::Features(Bolt11InvoiceFeatures::from_base32(field_data)?)),
467 // "A reader MUST skip over unknown fields"
468 Err(Bolt11ParseError::Skip)
474 impl FromBase32 for Sha256 {
475 type Err = Bolt11ParseError;
477 fn from_base32(field_data: &[u5]) -> Result<Sha256, Bolt11ParseError> {
478 if field_data.len() != 52 {
479 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
480 Err(Bolt11ParseError::Skip)
482 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
483 .expect("length was checked before (52 u5 -> 32 u8)")))
488 impl FromBase32 for Description {
489 type Err = Bolt11ParseError;
491 fn from_base32(field_data: &[u5]) -> Result<Description, Bolt11ParseError> {
492 let bytes = Vec::<u8>::from_base32(field_data)?;
493 let description = String::from(str::from_utf8(&bytes)?);
494 Ok(Description::new(description).expect(
495 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
500 impl FromBase32 for PayeePubKey {
501 type Err = Bolt11ParseError;
503 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, Bolt11ParseError> {
504 if field_data.len() != 53 {
505 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
506 Err(Bolt11ParseError::Skip)
508 let data_bytes = Vec::<u8>::from_base32(field_data)?;
509 let pub_key = PublicKey::from_slice(&data_bytes)?;
515 impl FromBase32 for ExpiryTime {
516 type Err = Bolt11ParseError;
518 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, Bolt11ParseError> {
519 match parse_u64_be(field_data)
520 .map(ExpiryTime::from_seconds)
523 None => Err(Bolt11ParseError::IntegerOverflowError),
528 impl FromBase32 for MinFinalCltvExpiryDelta {
529 type Err = Bolt11ParseError;
531 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
532 let expiry = parse_u64_be(field_data);
533 if let Some(expiry) = expiry {
534 Ok(MinFinalCltvExpiryDelta(expiry))
536 Err(Bolt11ParseError::IntegerOverflowError)
541 impl FromBase32 for Fallback {
542 type Err = Bolt11ParseError;
544 fn from_base32(field_data: &[u5]) -> Result<Fallback, Bolt11ParseError> {
545 if field_data.is_empty() {
546 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
549 let version = field_data[0];
550 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
552 match version.to_u8() {
554 if bytes.len() < 2 || bytes.len() > 40 {
555 return Err(Bolt11ParseError::InvalidSegWitProgramLength);
557 let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
558 Ok(Fallback::SegWitProgram {
564 let pkh = match PubkeyHash::from_slice(&bytes) {
566 Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
568 Ok(Fallback::PubKeyHash(pkh))
571 let sh = match ScriptHash::from_slice(&bytes) {
573 Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidScriptHashLength),
575 Ok(Fallback::ScriptHash(sh))
577 _ => Err(Bolt11ParseError::Skip)
582 impl FromBase32 for PrivateRoute {
583 type Err = Bolt11ParseError;
585 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, Bolt11ParseError> {
586 let bytes = Vec::<u8>::from_base32(field_data)?;
588 if bytes.len() % 51 != 0 {
589 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
592 let mut route_hops = Vec::<RouteHintHop>::new();
594 let mut bytes = bytes.as_slice();
595 while !bytes.is_empty() {
596 let hop_bytes = &bytes[0..51];
597 bytes = &bytes[51..];
599 let mut channel_id: [u8; 8] = Default::default();
600 channel_id.copy_from_slice(&hop_bytes[33..41]);
602 let hop = RouteHintHop {
603 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
604 short_channel_id: u64::from_be_bytes(channel_id),
606 base_msat: u32::from_be_bytes(hop_bytes[41..45].try_into().expect("slice too big?")),
607 proportional_millionths: u32::from_be_bytes(hop_bytes[45..49].try_into().expect("slice too big?")),
609 cltv_expiry_delta: u16::from_be_bytes(hop_bytes[49..51].try_into().expect("slice too big?")),
610 htlc_minimum_msat: None,
611 htlc_maximum_msat: None,
614 route_hops.push(hop);
617 Ok(PrivateRoute(RouteHint(route_hops)))
621 impl Display for Bolt11ParseError {
622 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
624 // TODO: find a way to combine the first three arms (e as error::Error?)
625 Bolt11ParseError::Bech32Error(ref e) => {
626 write!(f, "Invalid bech32: {}", e)
628 Bolt11ParseError::ParseAmountError(ref e) => {
629 write!(f, "Invalid amount in hrp ({})", e)
631 Bolt11ParseError::MalformedSignature(ref e) => {
632 write!(f, "Invalid secp256k1 signature: {}", e)
634 Bolt11ParseError::DescriptionDecodeError(ref e) => {
635 write!(f, "Description is not a valid utf-8 string: {}", e)
637 Bolt11ParseError::InvalidSliceLength(ref function) => {
638 write!(f, "Slice in function {} had the wrong length", function)
640 Bolt11ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
641 Bolt11ParseError::UnknownCurrency => f.write_str("currency code unknown"),
642 Bolt11ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
643 Bolt11ParseError::MalformedHRP => f.write_str("malformed human readable part"),
644 Bolt11ParseError::TooShortDataPart => {
645 f.write_str("data part too short (should be at least 111 bech32 chars long)")
647 Bolt11ParseError::UnexpectedEndOfTaggedFields => {
648 f.write_str("tagged fields part ended unexpectedly")
650 Bolt11ParseError::PaddingError => f.write_str("some data field had bad padding"),
651 Bolt11ParseError::IntegerOverflowError => {
652 f.write_str("parsed integer doesn't fit into receiving type")
654 Bolt11ParseError::InvalidSegWitProgramLength => {
655 f.write_str("fallback SegWit program is too long or too short")
657 Bolt11ParseError::InvalidPubKeyHashLength => {
658 f.write_str("fallback public key hash has a length unequal 20 bytes")
660 Bolt11ParseError::InvalidScriptHashLength => {
661 f.write_str("fallback script hash has a length unequal 32 bytes")
663 Bolt11ParseError::InvalidRecoveryId => {
664 f.write_str("recovery id is out of range (should be in [0,3])")
666 Bolt11ParseError::Skip => {
667 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
673 impl Display for ParseOrSemanticError {
674 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
676 ParseOrSemanticError::ParseError(err) => err.fmt(f),
677 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
682 #[cfg(feature = "std")]
683 impl error::Error for Bolt11ParseError {}
685 #[cfg(feature = "std")]
686 impl error::Error for ParseOrSemanticError {}
688 macro_rules! from_error {
689 ($my_error:expr, $extern_error:ty) => {
690 impl From<$extern_error> for Bolt11ParseError {
691 fn from(e: $extern_error) -> Self {
698 from_error!(Bolt11ParseError::MalformedSignature, secp256k1::Error);
699 from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
700 from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);
702 impl From<bech32::Error> for Bolt11ParseError {
703 fn from(e: bech32::Error) -> Self {
705 bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
706 _ => Bolt11ParseError::Bech32Error(e)
711 impl From<Bolt11ParseError> for ParseOrSemanticError {
712 fn from(e: Bolt11ParseError) -> Self {
713 ParseOrSemanticError::ParseError(e)
717 impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
718 fn from(e: Bolt11SemanticError) -> Self {
719 ParseOrSemanticError::SemanticError(e)
725 use crate::de::Bolt11ParseError;
726 use secp256k1::PublicKey;
728 use bitcoin::hashes::sha256;
729 use std::str::FromStr;
731 const CHARSET_REV: [i8; 128] = [
732 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
733 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
734 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
735 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
736 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
737 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
738 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
739 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
742 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
745 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
750 fn test_parse_currency_prefix() {
753 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
754 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
755 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
756 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
757 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
758 assert_eq!("something_else".parse::<Currency>(), Err(Bolt11ParseError::UnknownCurrency))
762 fn test_parse_int_from_bytes_be() {
763 use crate::de::parse_u16_be;
765 assert_eq!(parse_u16_be(&[
766 u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(),
767 u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()]
769 assert_eq!(parse_u16_be(&[
770 u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(),
771 u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()]
776 fn test_parse_sha256_hash() {
778 use bech32::FromBase32;
780 let input = from_bech32(
781 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
784 let hash = sha256::Hash::from_str(
785 "0001020304050607080900010203040506070809000102030405060708090102"
787 let expected = Ok(Sha256(hash));
789 assert_eq!(Sha256::from_base32(&input), expected);
791 // make sure hashes of unknown length get skipped
792 let input_unexpected_length = from_bech32(
793 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
795 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
799 fn test_parse_description() {
800 use crate::Description;
801 use bech32::FromBase32;
803 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
804 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
805 assert_eq!(Description::from_base32(&input), expected);
809 fn test_parse_payee_pub_key() {
810 use crate::PayeePubKey;
811 use bech32::FromBase32;
813 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
815 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
816 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
817 0x0f, 0x93, 0x4d, 0xd9, 0xad
819 let expected = Ok(PayeePubKey(
820 PublicKey::from_slice(&pk_bytes[..]).unwrap()
823 assert_eq!(PayeePubKey::from_base32(&input), expected);
826 let input_unexpected_length = from_bech32(
827 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
829 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
833 fn test_parse_expiry_time() {
834 use crate::ExpiryTime;
835 use bech32::FromBase32;
837 let input = from_bech32("pu".as_bytes());
838 let expected = Ok(ExpiryTime::from_seconds(60));
839 assert_eq!(ExpiryTime::from_base32(&input), expected);
841 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
842 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(Bolt11ParseError::IntegerOverflowError));
846 fn test_parse_min_final_cltv_expiry_delta() {
847 use crate::MinFinalCltvExpiryDelta;
848 use bech32::FromBase32;
850 let input = from_bech32("pr".as_bytes());
851 let expected = Ok(MinFinalCltvExpiryDelta(35));
853 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
857 fn test_parse_fallback() {
859 use bech32::FromBase32;
860 use bitcoin::{PubkeyHash, ScriptHash};
861 use bitcoin::address::WitnessVersion;
862 use bitcoin::hashes::Hash;
866 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
867 Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
868 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
869 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
873 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
874 Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
875 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
876 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
880 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
881 Ok(Fallback::SegWitProgram {
882 version: WitnessVersion::V0,
883 program: Vec::from(&[
884 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
885 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
890 vec![u5::try_from_u8(21).unwrap(); 41],
891 Err(Bolt11ParseError::Skip)
895 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
898 vec![u5::try_from_u8(1).unwrap(); 81],
899 Err(Bolt11ParseError::InvalidSegWitProgramLength)
902 vec![u5::try_from_u8(17).unwrap(); 1],
903 Err(Bolt11ParseError::InvalidPubKeyHashLength)
906 vec![u5::try_from_u8(18).unwrap(); 1],
907 Err(Bolt11ParseError::InvalidScriptHashLength)
911 for (input, expected) in cases.into_iter() {
912 assert_eq!(Fallback::from_base32(&input), expected);
917 fn test_parse_route() {
918 use lightning::routing::gossip::RoutingFees;
919 use lightning::routing::router::{RouteHint, RouteHintHop};
920 use crate::PrivateRoute;
921 use bech32::FromBase32;
923 let input = from_bech32(
924 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
925 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
928 let mut expected = Vec::<RouteHintHop>::new();
929 expected.push(RouteHintHop {
930 src_node_id: PublicKey::from_slice(
932 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
933 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
934 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
937 short_channel_id: 0x0102030405060708,
940 proportional_millionths: 20,
942 cltv_expiry_delta: 3,
943 htlc_minimum_msat: None,
944 htlc_maximum_msat: None
946 expected.push(RouteHintHop {
947 src_node_id: PublicKey::from_slice(
949 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
950 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
951 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
954 short_channel_id: 0x030405060708090a,
957 proportional_millionths: 30,
959 cltv_expiry_delta: 4,
960 htlc_minimum_msat: None,
961 htlc_maximum_msat: None
964 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
967 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
968 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
973 fn test_payment_secret_and_features_de_and_ser() {
974 use lightning::ln::features::Bolt11InvoiceFeatures;
975 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
976 use crate::TaggedField::*;
977 use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart,
978 Currency, Sha256, PositiveTimestamp};
980 // Feature bits 9, 15, and 99 are set.
981 let expected_features = Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
982 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
983 let invoice = SignedRawBolt11Invoice {
984 raw_invoice: RawBolt11Invoice {
986 currency: Currency::Bitcoin,
987 raw_amount: Some(25),
988 si_prefix: Some(SiPrefix::Milli)
991 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
992 tagged_fields: vec ! [
993 PaymentHash(Sha256(sha256::Hash::from_str(
994 "0001020304050607080900010203040506070809000102030405060708090102"
996 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
997 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
998 Features(expected_features).into()]}
1000 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1001 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1002 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1003 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1004 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1005 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1006 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1007 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1008 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1009 RecoveryId::from_i32(1).unwrap()
1012 assert_eq!(invoice_str, invoice.to_string());
1014 invoice_str.parse(),
1020 fn test_raw_signed_invoice_deserialization() {
1021 use crate::TaggedField::*;
1022 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
1023 use crate::{SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256,
1027 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1028 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1029 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1030 Ok(SignedRawBolt11Invoice {
1031 raw_invoice: RawBolt11Invoice {
1033 currency: Currency::Bitcoin,
1038 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1039 tagged_fields: vec ! [
1040 PaymentHash(Sha256(sha256::Hash::from_str(
1041 "0001020304050607080900010203040506070809000102030405060708090102"
1042 ).unwrap())).into(),
1044 crate::Description::new(
1045 "Please consider supporting this project".to_owned()
1052 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1053 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1054 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1056 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1058 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1059 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1060 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1061 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1062 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1063 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1065 RecoveryId::from_i32(0).unwrap()