1 #[cfg(feature = "std")]
4 use core::fmt::{Display, Formatter};
5 use core::num::ParseIntError;
7 use core::str::FromStr;
10 use bech32::{u5, FromBase32};
12 use bitcoin_hashes::Hash;
13 use bitcoin_hashes::sha256;
14 use crate::prelude::*;
15 use lightning::ln::PaymentSecret;
16 use lightning::routing::network_graph::RoutingFees;
17 use lightning::routing::router::{RouteHint, RouteHintHop};
19 use num_traits::{CheckedAdd, CheckedMul};
22 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
23 use secp256k1::key::PublicKey;
25 use super::{Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiry, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
26 SemanticError, PrivateRoute, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawInvoice, constants, SignedRawInvoice,
27 RawDataPart, InvoiceFeatures};
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::ParseError> {
49 if read_symbol == 'l' {
52 Err(super::ParseError::MalformedHRP)
56 if read_symbol == 'n' {
59 Err(super::ParseError::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::ParseError::UnknownSiPrefix)
85 States::ParseAmountSiPrefix => Err(super::ParseError::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::ParseError> {
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::ParseError> {
160 let mut sm = StateMachine::new();
161 for c in input.chars() {
166 return Err(super::ParseError::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 = ParseError;
184 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
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(ParseError::UnknownCurrency)
196 impl FromStr for SiPrefix {
197 type Err = ParseError;
199 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
201 match currency_prefix {
206 _ => Err(ParseError::UnknownSiPrefix)
212 /// use lightning_invoice::Invoice;
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::<Invoice>().is_ok());
229 impl FromStr for Invoice {
230 type Err = ParseOrSemanticError;
232 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
233 let signed = s.parse::<SignedRawInvoice>()?;
234 Ok(Invoice::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::<Invoice>();
255 /// let parsed_2 = match invoice.parse::<SignedRawInvoice>() {
256 /// Ok(signed) => match Invoice::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 SignedRawInvoice {
267 type Err = ParseError;
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(ParseError::Bech32Error(bech32::Error::InvalidChecksum));
278 if data.len() < 104 {
279 return Err(ParseError::TooShortDataPart);
282 let raw_hrp: RawHrp = hrp.parse()?;
283 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
285 Ok(SignedRawInvoice {
286 raw_invoice: RawInvoice {
290 hash: RawInvoice::hash_from_parts(
292 &data[..data.len()-104]
294 signature: InvoiceSignature::from_base32(&data[data.len()-104..])?,
299 impl FromStr for RawHrp {
300 type Err = ParseError;
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(ParseError::IntegerOverflowError);
328 si_prefix: si_prefix,
333 impl FromBase32 for RawDataPart {
334 type Err = ParseError;
336 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
337 if data.len() < 7 { // timestamp length
338 return Err(ParseError::TooShortDataPart);
341 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
342 let tagged = parse_tagged_parts(&data[7..])?;
345 timestamp: timestamp,
346 tagged_fields: tagged,
351 impl FromBase32 for PositiveTimestamp {
352 type Err = ParseError;
354 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
356 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
358 let timestamp: u64 = parse_int_be(b32, 32)
359 .expect("7*5bit < 64bit, no overflow possible");
360 match PositiveTimestamp::from_unix_timestamp(timestamp) {
362 Err(_) => unreachable!(),
367 impl FromBase32 for InvoiceSignature {
368 type Err = ParseError;
369 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
370 if signature.len() != 104 {
371 return Err(ParseError::InvalidSliceLength("InvoiceSignature::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(InvoiceSignature(RecoverableSignature::from_compact(
384 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
385 where T: CheckedAdd + CheckedMul + From<u8> + Default,
388 digits.iter().fold(Some(Default::default()), |acc, b|
390 .and_then(|x| x.checked_mul(&base))
391 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
395 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
396 let mut parts = Vec::<RawTaggedField>::new();
399 while !data.is_empty() {
401 return Err(ParseError::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_int_be(&data[1..3], 32).expect("can't overflow");
407 let last_element = 3 + len;
409 if data.len() < last_element {
410 return Err(ParseError::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(ParseError::Skip)|Err(ParseError::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 = ParseError;
435 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
437 return Err(ParseError::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 =>
455 Ok(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry::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_FEATURES =>
463 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
465 // "A reader MUST skip over unknown fields"
466 Err(ParseError::Skip)
472 impl FromBase32 for Sha256 {
473 type Err = ParseError;
475 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
476 if field_data.len() != 52 {
477 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
478 Err(ParseError::Skip)
480 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
481 .expect("length was checked before (52 u5 -> 32 u8)")))
486 impl FromBase32 for Description {
487 type Err = ParseError;
489 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
490 let bytes = Vec::<u8>::from_base32(field_data)?;
491 let description = String::from(str::from_utf8(&bytes)?);
492 Ok(Description::new(description).expect(
493 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
498 impl FromBase32 for PayeePubKey {
499 type Err = ParseError;
501 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
502 if field_data.len() != 53 {
503 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
504 Err(ParseError::Skip)
506 let data_bytes = Vec::<u8>::from_base32(field_data)?;
507 let pub_key = PublicKey::from_slice(&data_bytes)?;
513 impl FromBase32 for ExpiryTime {
514 type Err = ParseError;
516 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
517 match parse_int_be::<u64, u5>(field_data, 32)
518 .map(|t| ExpiryTime::from_seconds(t))
521 None => Err(ParseError::IntegerOverflowError),
526 impl FromBase32 for MinFinalCltvExpiry {
527 type Err = ParseError;
529 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiry, ParseError> {
530 let expiry = parse_int_be::<u64, u5>(field_data, 32);
531 if let Some(expiry) = expiry {
532 Ok(MinFinalCltvExpiry(expiry))
534 Err(ParseError::IntegerOverflowError)
539 impl FromBase32 for Fallback {
540 type Err = ParseError;
542 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
543 if field_data.len() < 1 {
544 return Err(ParseError::UnexpectedEndOfTaggedFields);
547 let version = field_data[0];
548 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
550 match version.to_u8() {
552 if bytes.len() < 2 || bytes.len() > 40 {
553 return Err(ParseError::InvalidSegWitProgramLength);
556 Ok(Fallback::SegWitProgram {
562 if bytes.len() != 20 {
563 return Err(ParseError::InvalidPubKeyHashLength);
565 //TODO: refactor once const generics are available
566 let mut pkh = [0u8; 20];
567 pkh.copy_from_slice(&bytes);
568 Ok(Fallback::PubKeyHash(pkh))
571 if bytes.len() != 20 {
572 return Err(ParseError::InvalidScriptHashLength);
574 let mut sh = [0u8; 20];
575 sh.copy_from_slice(&bytes);
576 Ok(Fallback::ScriptHash(sh))
578 _ => Err(ParseError::Skip)
583 impl FromBase32 for PrivateRoute {
584 type Err = ParseError;
586 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, ParseError> {
587 let bytes = Vec::<u8>::from_base32(field_data)?;
589 if bytes.len() % 51 != 0 {
590 return Err(ParseError::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: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
607 base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
608 proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
610 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).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 /// Errors that indicate what is wrong with the invoice. They have some granularity for debug
623 /// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
624 #[allow(missing_docs)]
625 #[derive(PartialEq, Debug, Clone)]
626 pub enum ParseError {
627 Bech32Error(bech32::Error),
628 ParseAmountError(ParseIntError),
629 MalformedSignature(secp256k1::Error),
635 UnexpectedEndOfTaggedFields,
636 DescriptionDecodeError(str::Utf8Error),
638 IntegerOverflowError,
639 InvalidSegWitProgramLength,
640 InvalidPubKeyHashLength,
641 InvalidScriptHashLength,
643 InvalidSliceLength(String),
645 /// Not an error, but used internally to signal that a part of the invoice should be ignored
646 /// according to BOLT11
650 /// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
651 /// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
652 /// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
653 #[derive(PartialEq, Debug, Clone)]
654 pub enum ParseOrSemanticError {
655 /// The invoice couldn't be decoded
656 ParseError(ParseError),
658 /// The invoice could be decoded but violates the BOLT11 standard
659 SemanticError(::SemanticError),
662 impl Display for ParseError {
663 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
665 // TODO: find a way to combine the first three arms (e as error::Error?)
666 ParseError::Bech32Error(ref e) => {
667 write!(f, "Invalid bech32: {}", e)
669 ParseError::ParseAmountError(ref e) => {
670 write!(f, "Invalid amount in hrp ({})", e)
672 ParseError::MalformedSignature(ref e) => {
673 write!(f, "Invalid secp256k1 signature: {}", e)
675 ParseError::DescriptionDecodeError(ref e) => {
676 write!(f, "Description is not a valid utf-8 string: {}", e)
678 ParseError::InvalidSliceLength(ref function) => {
679 write!(f, "Slice in function {} had the wrong length", function)
681 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
682 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
683 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
684 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
685 ParseError::TooShortDataPart => {
686 f.write_str("data part too short (should be at least 111 bech32 chars long)")
688 ParseError::UnexpectedEndOfTaggedFields => {
689 f.write_str("tagged fields part ended unexpectedly")
691 ParseError::PaddingError => f.write_str("some data field had bad padding"),
692 ParseError::IntegerOverflowError => {
693 f.write_str("parsed integer doesn't fit into receiving type")
695 ParseError::InvalidSegWitProgramLength => {
696 f.write_str("fallback SegWit program is too long or too short")
698 ParseError::InvalidPubKeyHashLength => {
699 f.write_str("fallback public key hash has a length unequal 20 bytes")
701 ParseError::InvalidScriptHashLength => {
702 f.write_str("fallback script hash has a length unequal 32 bytes")
704 ParseError::InvalidRecoveryId => {
705 f.write_str("recovery id is out of range (should be in [0,3])")
707 ParseError::Skip => {
708 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
714 impl Display for ParseOrSemanticError {
715 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
717 ParseOrSemanticError::ParseError(err) => err.fmt(f),
718 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
723 #[cfg(feature = "std")]
724 impl error::Error for ParseError {}
726 #[cfg(feature = "std")]
727 impl error::Error for ParseOrSemanticError {}
729 macro_rules! from_error {
730 ($my_error:expr, $extern_error:ty) => {
731 impl From<$extern_error> for ParseError {
732 fn from(e: $extern_error) -> Self {
739 from_error!(ParseError::MalformedSignature, secp256k1::Error);
740 from_error!(ParseError::ParseAmountError, ParseIntError);
741 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
743 impl From<bech32::Error> for ParseError {
744 fn from(e: bech32::Error) -> Self {
746 bech32::Error::InvalidPadding => ParseError::PaddingError,
747 _ => ParseError::Bech32Error(e)
752 impl From<ParseError> for ParseOrSemanticError {
753 fn from(e: ParseError) -> Self {
754 ParseOrSemanticError::ParseError(e)
758 impl From<::SemanticError> for ParseOrSemanticError {
759 fn from(e: SemanticError) -> Self {
760 ParseOrSemanticError::SemanticError(e)
767 use secp256k1::PublicKey;
769 use bitcoin_hashes::hex::FromHex;
770 use bitcoin_hashes::sha256;
772 const CHARSET_REV: [i8; 128] = [
773 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
774 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
775 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
776 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
777 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
778 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
779 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
780 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
783 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
786 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
791 fn test_parse_currency_prefix() {
794 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
795 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
796 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
797 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
798 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
799 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
803 fn test_parse_int_from_bytes_be() {
804 use de::parse_int_be;
806 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
807 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
808 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
809 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
813 fn test_parse_sha256_hash() {
815 use bech32::FromBase32;
817 let input = from_bech32(
818 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
821 let hash = sha256::Hash::from_hex(
822 "0001020304050607080900010203040506070809000102030405060708090102"
824 let expected = Ok(Sha256(hash));
826 assert_eq!(Sha256::from_base32(&input), expected);
828 // make sure hashes of unknown length get skipped
829 let input_unexpected_length = from_bech32(
830 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
832 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
836 fn test_parse_description() {
838 use bech32::FromBase32;
840 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
841 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
842 assert_eq!(Description::from_base32(&input), expected);
846 fn test_parse_payee_pub_key() {
848 use bech32::FromBase32;
850 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
852 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
853 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
854 0x0f, 0x93, 0x4d, 0xd9, 0xad
856 let expected = Ok(PayeePubKey(
857 PublicKey::from_slice(&pk_bytes[..]).unwrap()
860 assert_eq!(PayeePubKey::from_base32(&input), expected);
863 let input_unexpected_length = from_bech32(
864 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
866 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
870 fn test_parse_expiry_time() {
872 use bech32::FromBase32;
874 let input = from_bech32("pu".as_bytes());
875 let expected = Ok(ExpiryTime::from_seconds(60));
876 assert_eq!(ExpiryTime::from_base32(&input), expected);
878 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
879 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
883 fn test_parse_min_final_cltv_expiry() {
884 use ::MinFinalCltvExpiry;
885 use bech32::FromBase32;
887 let input = from_bech32("pr".as_bytes());
888 let expected = Ok(MinFinalCltvExpiry(35));
890 assert_eq!(MinFinalCltvExpiry::from_base32(&input), expected);
894 fn test_parse_fallback() {
896 use bech32::FromBase32;
900 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
901 Ok(Fallback::PubKeyHash([
902 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
903 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
907 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
908 Ok(Fallback::ScriptHash([
909 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
910 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
914 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
915 Ok(Fallback::SegWitProgram {
916 version: u5::try_from_u8(0).unwrap(),
917 program: Vec::from(&[
918 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
919 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
924 vec![u5::try_from_u8(21).unwrap(); 41],
925 Err(ParseError::Skip)
929 Err(ParseError::UnexpectedEndOfTaggedFields)
932 vec![u5::try_from_u8(1).unwrap(); 81],
933 Err(ParseError::InvalidSegWitProgramLength)
936 vec![u5::try_from_u8(17).unwrap(); 1],
937 Err(ParseError::InvalidPubKeyHashLength)
940 vec![u5::try_from_u8(18).unwrap(); 1],
941 Err(ParseError::InvalidScriptHashLength)
945 for (input, expected) in cases.into_iter() {
946 assert_eq!(Fallback::from_base32(&input), expected);
951 fn test_parse_route() {
952 use lightning::routing::network_graph::RoutingFees;
953 use lightning::routing::router::{RouteHint, RouteHintHop};
955 use bech32::FromBase32;
956 use de::parse_int_be;
958 let input = from_bech32(
959 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
960 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
963 let mut expected = Vec::<RouteHintHop>::new();
964 expected.push(RouteHintHop {
965 src_node_id: PublicKey::from_slice(
967 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
968 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
969 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
972 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
975 proportional_millionths: 20,
977 cltv_expiry_delta: 3,
978 htlc_minimum_msat: None,
979 htlc_maximum_msat: None
981 expected.push(RouteHintHop {
982 src_node_id: PublicKey::from_slice(
984 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
985 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
986 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
989 short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
992 proportional_millionths: 30,
994 cltv_expiry_delta: 4,
995 htlc_minimum_msat: None,
996 htlc_maximum_msat: None
999 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
1002 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
1003 Err(ParseError::UnexpectedEndOfTaggedFields)
1008 fn test_payment_secret_and_features_de_and_ser() {
1009 use lightning::ln::features::InvoiceFeatures;
1010 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1012 use {SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
1013 Currency, Sha256, PositiveTimestamp};
1015 // Feature bits 9, 15, and 99 are set.
1016 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
1017 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
1018 let invoice = SignedRawInvoice {
1019 raw_invoice: RawInvoice {
1021 currency: Currency::Bitcoin,
1022 raw_amount: Some(25),
1023 si_prefix: Some(SiPrefix::Milli)
1026 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1027 tagged_fields: vec ! [
1028 PaymentHash(Sha256(sha256::Hash::from_hex(
1029 "0001020304050607080900010203040506070809000102030405060708090102"
1030 ).unwrap())).into(),
1031 Description(::Description::new("coffee beans".to_owned()).unwrap()).into(),
1032 PaymentSecret(::PaymentSecret([17; 32])).into(),
1033 Features(expected_features).into()]}
1035 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1036 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1037 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1038 signature: InvoiceSignature(RecoverableSignature::from_compact(
1039 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1040 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1041 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1042 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1043 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1044 RecoveryId::from_i32(1).unwrap()
1047 assert_eq!(invoice_str, invoice.to_string());
1049 invoice_str.parse(),
1055 fn test_raw_signed_invoice_deserialization() {
1057 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1058 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1062 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1063 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1064 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1065 Ok(SignedRawInvoice {
1066 raw_invoice: RawInvoice {
1068 currency: Currency::Bitcoin,
1073 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1074 tagged_fields: vec ! [
1075 PaymentHash(Sha256(sha256::Hash::from_hex(
1076 "0001020304050607080900010203040506070809000102030405060708090102"
1077 ).unwrap())).into(),
1080 "Please consider supporting this project".to_owned()
1087 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1088 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1089 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1091 signature: InvoiceSignature(RecoverableSignature::from_compact(
1093 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1094 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1095 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1096 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1097 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1098 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1100 RecoveryId::from_i32(0).unwrap()