1 #[cfg(feature = "std")]
3 #[cfg(not(feature = "std"))]
4 use core::convert::TryFrom;
6 use core::fmt::{Display, Formatter};
7 use core::num::ParseIntError;
9 use core::str::FromStr;
11 use bech32::{u5, FromBase32};
13 use bitcoin::{PubkeyHash, ScriptHash};
14 use bitcoin::address::WitnessVersion;
15 use bitcoin::hashes::Hash;
16 use bitcoin::hashes::sha256;
17 use crate::prelude::*;
18 use lightning::ln::PaymentSecret;
19 use lightning::routing::gossip::RoutingFees;
20 use lightning::routing::router::{RouteHint, RouteHintHop};
22 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
23 use secp256k1::PublicKey;
25 use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
26 Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
27 constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures};
29 use self::hrp_sm::parse_hrp;
31 /// State machine to parse the hrp
35 #[derive(PartialEq, Eq, Debug)]
46 fn next_state(&self, read_symbol: char) -> Result<States, super::Bolt11ParseError> {
49 if read_symbol == 'l' {
52 Err(super::Bolt11ParseError::MalformedHRP)
56 if read_symbol == 'n' {
59 Err(super::Bolt11ParseError::MalformedHRP)
63 if !read_symbol.is_numeric() {
64 Ok(States::ParseCurrencyPrefix)
66 Ok(States::ParseAmountNumber)
69 States::ParseCurrencyPrefix => {
70 if !read_symbol.is_numeric() {
71 Ok(States::ParseCurrencyPrefix)
73 Ok(States::ParseAmountNumber)
76 States::ParseAmountNumber => {
77 if read_symbol.is_numeric() {
78 Ok(States::ParseAmountNumber)
79 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
80 Ok(States::ParseAmountSiPrefix)
82 Err(super::Bolt11ParseError::UnknownSiPrefix)
85 States::ParseAmountSiPrefix => Err(super::Bolt11ParseError::MalformedHRP),
89 fn is_final(&self) -> bool {
90 !(*self == States::ParseL || *self == States::ParseN)
98 currency_prefix: Option<Range<usize>>,
99 amount_number: Option<Range<usize>>,
100 amount_si_prefix: Option<Range<usize>>,
104 fn new() -> StateMachine {
106 state: States::Start,
108 currency_prefix: None,
110 amount_si_prefix: None,
114 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
115 let new_range = match *range {
116 None => Range {start: position, end: position + 1},
117 Some(ref r) => Range {start: r.start, end: r.end + 1},
119 *range = Some(new_range);
122 fn step(&mut self, c: char) -> Result<(), super::Bolt11ParseError> {
123 let next_state = self.state.next_state(c)?;
125 States::ParseCurrencyPrefix => {
126 StateMachine::update_range(&mut self.currency_prefix, self.position)
128 States::ParseAmountNumber => {
129 StateMachine::update_range(&mut self.amount_number, self.position)
131 States::ParseAmountSiPrefix => {
132 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
138 self.state = next_state;
142 fn is_final(&self) -> bool {
143 self.state.is_final()
146 fn currency_prefix(&self) -> &Option<Range<usize>> {
147 &self.currency_prefix
150 fn amount_number(&self) -> &Option<Range<usize>> {
154 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
155 &self.amount_si_prefix
159 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> {
160 let mut sm = StateMachine::new();
161 for c in input.chars() {
166 return Err(super::Bolt11ParseError::MalformedHRP);
169 let currency = sm.currency_prefix().clone()
170 .map(|r| &input[r]).unwrap_or("");
171 let amount = sm.amount_number().clone()
172 .map(|r| &input[r]).unwrap_or("");
173 let si = sm.amount_si_prefix().clone()
174 .map(|r| &input[r]).unwrap_or("");
176 Ok((currency, amount, si))
181 impl FromStr for super::Currency {
182 type Err = Bolt11ParseError;
184 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
185 match currency_prefix {
186 "bc" => Ok(Currency::Bitcoin),
187 "tb" => Ok(Currency::BitcoinTestnet),
188 "bcrt" => Ok(Currency::Regtest),
189 "sb" => Ok(Currency::Simnet),
190 "tbs" => Ok(Currency::Signet),
191 _ => Err(Bolt11ParseError::UnknownCurrency)
196 impl FromStr for SiPrefix {
197 type Err = Bolt11ParseError;
199 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
200 use crate::SiPrefix::*;
201 match currency_prefix {
206 _ => Err(Bolt11ParseError::UnknownSiPrefix)
212 /// use lightning_invoice::Bolt11Invoice;
215 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
216 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
217 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
218 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
219 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
220 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
221 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
222 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
223 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
224 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
225 /// j5r6drg6k6zcqj0fcwg";
227 /// assert!(invoice.parse::<Bolt11Invoice>().is_ok());
229 impl FromStr for Bolt11Invoice {
230 type Err = ParseOrSemanticError;
232 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
233 let signed = s.parse::<SignedRawBolt11Invoice>()?;
234 Ok(Bolt11Invoice::from_signed(signed)?)
239 /// use lightning_invoice::*;
241 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
242 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
243 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
244 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
245 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
246 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
247 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
248 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
249 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
250 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
251 /// j5r6drg6k6zcqj0fcwg";
253 /// let parsed_1 = invoice.parse::<Bolt11Invoice>();
255 /// let parsed_2 = match invoice.parse::<SignedRawBolt11Invoice>() {
256 /// Ok(signed) => match Bolt11Invoice::from_signed(signed) {
257 /// Ok(invoice) => Ok(invoice),
258 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
260 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
263 /// assert!(parsed_1.is_ok());
264 /// assert_eq!(parsed_1, parsed_2);
266 impl FromStr for SignedRawBolt11Invoice {
267 type Err = Bolt11ParseError;
269 fn from_str(s: &str) -> Result<Self, Self::Err> {
270 let (hrp, data, var) = bech32::decode(s)?;
272 if var == bech32::Variant::Bech32m {
273 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
274 // we didn't support Bech32m (which lightning does not use).
275 return Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum));
278 if data.len() < 104 {
279 return Err(Bolt11ParseError::TooShortDataPart);
282 let raw_hrp: RawHrp = hrp.parse()?;
283 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
285 Ok(SignedRawBolt11Invoice {
286 raw_invoice: RawBolt11Invoice {
290 hash: RawBolt11Invoice::hash_from_parts(
292 &data[..data.len()-104]
294 signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
299 impl FromStr for RawHrp {
300 type Err = Bolt11ParseError;
302 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
303 let parts = parse_hrp(hrp)?;
305 let currency = parts.0.parse::<Currency>()?;
307 let amount = if !parts.1.is_empty() {
308 Some(parts.1.parse::<u64>()?)
313 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
316 let si: SiPrefix = parts.2.parse()?;
317 if let Some(amt) = amount {
318 if amt.checked_mul(si.multiplier()).is_none() {
319 return Err(Bolt11ParseError::IntegerOverflowError);
333 impl FromBase32 for RawDataPart {
334 type Err = Bolt11ParseError;
336 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
337 if data.len() < 7 { // timestamp length
338 return Err(Bolt11ParseError::TooShortDataPart);
341 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
342 let tagged = parse_tagged_parts(&data[7..])?;
346 tagged_fields: tagged,
351 impl FromBase32 for PositiveTimestamp {
352 type Err = Bolt11ParseError;
354 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
356 return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
358 let timestamp: u64 = parse_u64_be(b32)
359 .expect("7*5bit < 64bit, no overflow possible");
360 match PositiveTimestamp::from_unix_timestamp(timestamp) {
362 Err(_) => unreachable!(),
367 impl FromBase32 for Bolt11InvoiceSignature {
368 type Err = Bolt11ParseError;
369 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
370 if signature.len() != 104 {
371 return Err(Bolt11ParseError::InvalidSliceLength("Bolt11InvoiceSignature::from_base32()".into()));
373 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
374 let signature = &recoverable_signature_bytes[0..64];
375 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
377 Ok(Bolt11InvoiceSignature(RecoverableSignature::from_compact(
384 macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
385 fn $name(digits: &[u5]) -> Option<$ty> {
386 digits.iter().fold(Some(Default::default()), |acc, b|
388 .and_then(|x| x.checked_mul(32))
389 .and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
393 define_parse_int_be!(parse_u16_be, u16);
394 define_parse_int_be!(parse_u64_be, u64);
396 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
397 let mut parts = Vec::<RawTaggedField>::new();
400 while !data.is_empty() {
402 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
405 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
406 // parse the length to find the end of the tagged field's data
407 let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize;
408 let last_element = 3 + len;
410 if data.len() < last_element {
411 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
414 // Get the tagged field's data slice
415 let field = &data[0..last_element];
417 // Set data slice to remaining data
418 data = &data[last_element..];
420 match TaggedField::from_base32(field) {
422 parts.push(RawTaggedField::KnownSemantics(field))
424 Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
425 parts.push(RawTaggedField::UnknownSemantics(field.into()))
427 Err(e) => {return Err(e)}
433 impl FromBase32 for TaggedField {
434 type Err = Bolt11ParseError;
436 fn from_base32(field: &[u5]) -> Result<TaggedField, Bolt11ParseError> {
438 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
442 let field_data = &field[3..];
445 constants::TAG_PAYMENT_HASH =>
446 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
447 constants::TAG_DESCRIPTION =>
448 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
449 constants::TAG_PAYEE_PUB_KEY =>
450 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
451 constants::TAG_DESCRIPTION_HASH =>
452 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
453 constants::TAG_EXPIRY_TIME =>
454 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
455 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA =>
456 Ok(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta::from_base32(field_data)?)),
457 constants::TAG_FALLBACK =>
458 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
459 constants::TAG_PRIVATE_ROUTE =>
460 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
461 constants::TAG_PAYMENT_SECRET =>
462 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
463 constants::TAG_PAYMENT_METADATA =>
464 Ok(TaggedField::PaymentMetadata(Vec::<u8>::from_base32(field_data)?)),
465 constants::TAG_FEATURES =>
466 Ok(TaggedField::Features(Bolt11InvoiceFeatures::from_base32(field_data)?)),
468 // "A reader MUST skip over unknown fields"
469 Err(Bolt11ParseError::Skip)
475 impl FromBase32 for Sha256 {
476 type Err = Bolt11ParseError;
478 fn from_base32(field_data: &[u5]) -> Result<Sha256, Bolt11ParseError> {
479 if field_data.len() != 52 {
480 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
481 Err(Bolt11ParseError::Skip)
483 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
484 .expect("length was checked before (52 u5 -> 32 u8)")))
489 impl FromBase32 for Description {
490 type Err = Bolt11ParseError;
492 fn from_base32(field_data: &[u5]) -> Result<Description, Bolt11ParseError> {
493 let bytes = Vec::<u8>::from_base32(field_data)?;
494 let description = String::from(str::from_utf8(&bytes)?);
495 Ok(Description::new(description).expect(
496 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
501 impl FromBase32 for PayeePubKey {
502 type Err = Bolt11ParseError;
504 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, Bolt11ParseError> {
505 if field_data.len() != 53 {
506 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
507 Err(Bolt11ParseError::Skip)
509 let data_bytes = Vec::<u8>::from_base32(field_data)?;
510 let pub_key = PublicKey::from_slice(&data_bytes)?;
516 impl FromBase32 for ExpiryTime {
517 type Err = Bolt11ParseError;
519 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, Bolt11ParseError> {
520 match parse_u64_be(field_data)
521 .map(ExpiryTime::from_seconds)
524 None => Err(Bolt11ParseError::IntegerOverflowError),
529 impl FromBase32 for MinFinalCltvExpiryDelta {
530 type Err = Bolt11ParseError;
532 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
533 let expiry = parse_u64_be(field_data);
534 if let Some(expiry) = expiry {
535 Ok(MinFinalCltvExpiryDelta(expiry))
537 Err(Bolt11ParseError::IntegerOverflowError)
542 impl FromBase32 for Fallback {
543 type Err = Bolt11ParseError;
545 fn from_base32(field_data: &[u5]) -> Result<Fallback, Bolt11ParseError> {
546 if field_data.is_empty() {
547 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
550 let version = field_data[0];
551 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
553 match version.to_u8() {
555 if bytes.len() < 2 || bytes.len() > 40 {
556 return Err(Bolt11ParseError::InvalidSegWitProgramLength);
558 let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
559 Ok(Fallback::SegWitProgram {
565 let pkh = match PubkeyHash::from_slice(&bytes) {
567 Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
569 Ok(Fallback::PubKeyHash(pkh))
572 let sh = match ScriptHash::from_slice(&bytes) {
574 Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidScriptHashLength),
576 Ok(Fallback::ScriptHash(sh))
578 _ => Err(Bolt11ParseError::Skip)
583 impl FromBase32 for PrivateRoute {
584 type Err = Bolt11ParseError;
586 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, Bolt11ParseError> {
587 let bytes = Vec::<u8>::from_base32(field_data)?;
589 if bytes.len() % 51 != 0 {
590 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
593 let mut route_hops = Vec::<RouteHintHop>::new();
595 let mut bytes = bytes.as_slice();
596 while !bytes.is_empty() {
597 let hop_bytes = &bytes[0..51];
598 bytes = &bytes[51..];
600 let mut channel_id: [u8; 8] = Default::default();
601 channel_id.copy_from_slice(&hop_bytes[33..41]);
603 let hop = RouteHintHop {
604 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
605 short_channel_id: u64::from_be_bytes(channel_id),
607 base_msat: u32::from_be_bytes(hop_bytes[41..45].try_into().expect("slice too big?")),
608 proportional_millionths: u32::from_be_bytes(hop_bytes[45..49].try_into().expect("slice too big?")),
610 cltv_expiry_delta: u16::from_be_bytes(hop_bytes[49..51].try_into().expect("slice too big?")),
611 htlc_minimum_msat: None,
612 htlc_maximum_msat: None,
615 route_hops.push(hop);
618 Ok(PrivateRoute(RouteHint(route_hops)))
622 impl Display for Bolt11ParseError {
623 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
625 // TODO: find a way to combine the first three arms (e as error::Error?)
626 Bolt11ParseError::Bech32Error(ref e) => {
627 write!(f, "Invalid bech32: {}", e)
629 Bolt11ParseError::ParseAmountError(ref e) => {
630 write!(f, "Invalid amount in hrp ({})", e)
632 Bolt11ParseError::MalformedSignature(ref e) => {
633 write!(f, "Invalid secp256k1 signature: {}", e)
635 Bolt11ParseError::DescriptionDecodeError(ref e) => {
636 write!(f, "Description is not a valid utf-8 string: {}", e)
638 Bolt11ParseError::InvalidSliceLength(ref function) => {
639 write!(f, "Slice in function {} had the wrong length", function)
641 Bolt11ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
642 Bolt11ParseError::UnknownCurrency => f.write_str("currency code unknown"),
643 Bolt11ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
644 Bolt11ParseError::MalformedHRP => f.write_str("malformed human readable part"),
645 Bolt11ParseError::TooShortDataPart => {
646 f.write_str("data part too short (should be at least 111 bech32 chars long)")
648 Bolt11ParseError::UnexpectedEndOfTaggedFields => {
649 f.write_str("tagged fields part ended unexpectedly")
651 Bolt11ParseError::PaddingError => f.write_str("some data field had bad padding"),
652 Bolt11ParseError::IntegerOverflowError => {
653 f.write_str("parsed integer doesn't fit into receiving type")
655 Bolt11ParseError::InvalidSegWitProgramLength => {
656 f.write_str("fallback SegWit program is too long or too short")
658 Bolt11ParseError::InvalidPubKeyHashLength => {
659 f.write_str("fallback public key hash has a length unequal 20 bytes")
661 Bolt11ParseError::InvalidScriptHashLength => {
662 f.write_str("fallback script hash has a length unequal 32 bytes")
664 Bolt11ParseError::InvalidRecoveryId => {
665 f.write_str("recovery id is out of range (should be in [0,3])")
667 Bolt11ParseError::Skip => {
668 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
674 impl Display for ParseOrSemanticError {
675 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
677 ParseOrSemanticError::ParseError(err) => err.fmt(f),
678 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
683 #[cfg(feature = "std")]
684 impl error::Error for Bolt11ParseError {}
686 #[cfg(feature = "std")]
687 impl error::Error for ParseOrSemanticError {}
689 macro_rules! from_error {
690 ($my_error:expr, $extern_error:ty) => {
691 impl From<$extern_error> for Bolt11ParseError {
692 fn from(e: $extern_error) -> Self {
699 from_error!(Bolt11ParseError::MalformedSignature, secp256k1::Error);
700 from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
701 from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);
703 impl From<bech32::Error> for Bolt11ParseError {
704 fn from(e: bech32::Error) -> Self {
706 bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
707 _ => Bolt11ParseError::Bech32Error(e)
712 impl From<Bolt11ParseError> for ParseOrSemanticError {
713 fn from(e: Bolt11ParseError) -> Self {
714 ParseOrSemanticError::ParseError(e)
718 impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
719 fn from(e: Bolt11SemanticError) -> Self {
720 ParseOrSemanticError::SemanticError(e)
726 use crate::de::Bolt11ParseError;
727 use secp256k1::PublicKey;
729 use bitcoin::hashes::sha256;
730 use std::str::FromStr;
732 const CHARSET_REV: [i8; 128] = [
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 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
736 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
737 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
738 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
739 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
740 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
743 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
746 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
751 fn test_parse_currency_prefix() {
754 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
755 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
756 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
757 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
758 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
759 assert_eq!("something_else".parse::<Currency>(), Err(Bolt11ParseError::UnknownCurrency))
763 fn test_parse_int_from_bytes_be() {
764 use crate::de::parse_u16_be;
766 assert_eq!(parse_u16_be(&[
767 u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(),
768 u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()]
770 assert_eq!(parse_u16_be(&[
771 u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(),
772 u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()]
777 fn test_parse_sha256_hash() {
779 use bech32::FromBase32;
781 let input = from_bech32(
782 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
785 let hash = sha256::Hash::from_str(
786 "0001020304050607080900010203040506070809000102030405060708090102"
788 let expected = Ok(Sha256(hash));
790 assert_eq!(Sha256::from_base32(&input), expected);
792 // make sure hashes of unknown length get skipped
793 let input_unexpected_length = from_bech32(
794 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
796 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
800 fn test_parse_description() {
801 use crate::Description;
802 use bech32::FromBase32;
804 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
805 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
806 assert_eq!(Description::from_base32(&input), expected);
810 fn test_parse_payee_pub_key() {
811 use crate::PayeePubKey;
812 use bech32::FromBase32;
814 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
816 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
817 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
818 0x0f, 0x93, 0x4d, 0xd9, 0xad
820 let expected = Ok(PayeePubKey(
821 PublicKey::from_slice(&pk_bytes[..]).unwrap()
824 assert_eq!(PayeePubKey::from_base32(&input), expected);
827 let input_unexpected_length = from_bech32(
828 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
830 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
834 fn test_parse_expiry_time() {
835 use crate::ExpiryTime;
836 use bech32::FromBase32;
838 let input = from_bech32("pu".as_bytes());
839 let expected = Ok(ExpiryTime::from_seconds(60));
840 assert_eq!(ExpiryTime::from_base32(&input), expected);
842 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
843 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(Bolt11ParseError::IntegerOverflowError));
847 fn test_parse_min_final_cltv_expiry_delta() {
848 use crate::MinFinalCltvExpiryDelta;
849 use bech32::FromBase32;
851 let input = from_bech32("pr".as_bytes());
852 let expected = Ok(MinFinalCltvExpiryDelta(35));
854 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
858 fn test_parse_fallback() {
860 use bech32::FromBase32;
861 use bitcoin::{PubkeyHash, ScriptHash};
862 use bitcoin::address::WitnessVersion;
863 use bitcoin::hashes::Hash;
867 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
868 Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
869 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
870 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
874 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
875 Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
876 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
877 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
881 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
882 Ok(Fallback::SegWitProgram {
883 version: WitnessVersion::V0,
884 program: Vec::from(&[
885 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
886 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
891 vec![u5::try_from_u8(21).unwrap(); 41],
892 Err(Bolt11ParseError::Skip)
896 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
899 vec![u5::try_from_u8(1).unwrap(); 81],
900 Err(Bolt11ParseError::InvalidSegWitProgramLength)
903 vec![u5::try_from_u8(17).unwrap(); 1],
904 Err(Bolt11ParseError::InvalidPubKeyHashLength)
907 vec![u5::try_from_u8(18).unwrap(); 1],
908 Err(Bolt11ParseError::InvalidScriptHashLength)
912 for (input, expected) in cases.into_iter() {
913 assert_eq!(Fallback::from_base32(&input), expected);
918 fn test_parse_route() {
919 use lightning::routing::gossip::RoutingFees;
920 use lightning::routing::router::{RouteHint, RouteHintHop};
921 use crate::PrivateRoute;
922 use bech32::FromBase32;
924 let input = from_bech32(
925 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
926 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
929 let mut expected = Vec::<RouteHintHop>::new();
930 expected.push(RouteHintHop {
931 src_node_id: PublicKey::from_slice(
933 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
934 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
935 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
938 short_channel_id: 0x0102030405060708,
941 proportional_millionths: 20,
943 cltv_expiry_delta: 3,
944 htlc_minimum_msat: None,
945 htlc_maximum_msat: None
947 expected.push(RouteHintHop {
948 src_node_id: PublicKey::from_slice(
950 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
951 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
952 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
955 short_channel_id: 0x030405060708090a,
958 proportional_millionths: 30,
960 cltv_expiry_delta: 4,
961 htlc_minimum_msat: None,
962 htlc_maximum_msat: None
965 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
968 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
969 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
974 fn test_payment_secret_and_features_de_and_ser() {
975 use lightning::ln::features::Bolt11InvoiceFeatures;
976 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
977 use crate::TaggedField::*;
978 use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart,
979 Currency, Sha256, PositiveTimestamp};
981 // Feature bits 9, 15, and 99 are set.
982 let expected_features = Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
983 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
984 let invoice = SignedRawBolt11Invoice {
985 raw_invoice: RawBolt11Invoice {
987 currency: Currency::Bitcoin,
988 raw_amount: Some(25),
989 si_prefix: Some(SiPrefix::Milli)
992 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
993 tagged_fields: vec ! [
994 PaymentHash(Sha256(sha256::Hash::from_str(
995 "0001020304050607080900010203040506070809000102030405060708090102"
997 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
998 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
999 Features(expected_features).into()]}
1001 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1002 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1003 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1004 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1005 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1006 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1007 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1008 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1009 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1010 RecoveryId::from_i32(1).unwrap()
1013 assert_eq!(invoice_str, invoice.to_string());
1015 invoice_str.parse(),
1021 fn test_raw_signed_invoice_deserialization() {
1022 use crate::TaggedField::*;
1023 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
1024 use crate::{SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256,
1028 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1029 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1030 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1031 Ok(SignedRawBolt11Invoice {
1032 raw_invoice: RawBolt11Invoice {
1034 currency: Currency::Bitcoin,
1039 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1040 tagged_fields: vec ! [
1041 PaymentHash(Sha256(sha256::Hash::from_str(
1042 "0001020304050607080900010203040506070809000102030405060708090102"
1043 ).unwrap())).into(),
1045 crate::Description::new(
1046 "Please consider supporting this project".to_owned()
1053 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1054 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1055 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1057 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1059 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1060 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1061 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1062 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1063 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1064 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1066 RecoveryId::from_i32(0).unwrap()