3 use std::fmt::{Display, Formatter};
4 use std::num::ParseIntError;
9 use bech32::{u5, FromBase32};
11 use bitcoin_hashes::Hash;
12 use bitcoin_hashes::sha256;
13 use lightning::ln::PaymentSecret;
14 use lightning::routing::network_graph::RoutingFees;
15 use lightning::routing::router::{RouteHint, RouteHintHop};
17 use num_traits::{CheckedAdd, CheckedMul};
20 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
21 use secp256k1::key::PublicKey;
23 use super::{Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiry, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
24 SemanticError, PrivateRoute, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawInvoice, constants, SignedRawInvoice,
25 RawDataPart, CreationError, InvoiceFeatures};
27 use self::hrp_sm::parse_hrp;
29 /// State machine to parse the hrp
33 #[derive(PartialEq, Eq, Debug)]
44 fn next_state(&self, read_symbol: char) -> Result<States, super::ParseError> {
47 if read_symbol == 'l' {
50 Err(super::ParseError::MalformedHRP)
54 if read_symbol == 'n' {
57 Err(super::ParseError::MalformedHRP)
61 if !read_symbol.is_numeric() {
62 Ok(States::ParseCurrencyPrefix)
64 Ok(States::ParseAmountNumber)
67 States::ParseCurrencyPrefix => {
68 if !read_symbol.is_numeric() {
69 Ok(States::ParseCurrencyPrefix)
71 Ok(States::ParseAmountNumber)
74 States::ParseAmountNumber => {
75 if read_symbol.is_numeric() {
76 Ok(States::ParseAmountNumber)
77 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
78 Ok(States::ParseAmountSiPrefix)
80 Err(super::ParseError::MalformedHRP)
83 States::ParseAmountSiPrefix => Err(super::ParseError::MalformedHRP),
87 fn is_final(&self) -> bool {
88 !(*self == States::ParseL || *self == States::ParseN)
96 currency_prefix: Option<Range<usize>>,
97 amount_number: Option<Range<usize>>,
98 amount_si_prefix: Option<Range<usize>>,
102 fn new() -> StateMachine {
104 state: States::Start,
106 currency_prefix: None,
108 amount_si_prefix: None,
112 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
113 let new_range = match *range {
114 None => Range {start: position, end: position + 1},
115 Some(ref r) => Range {start: r.start, end: r.end + 1},
117 *range = Some(new_range);
120 fn step(&mut self, c: char) -> Result<(), super::ParseError> {
121 let next_state = self.state.next_state(c)?;
123 States::ParseCurrencyPrefix => {
124 StateMachine::update_range(&mut self.currency_prefix, self.position)
126 States::ParseAmountNumber => {
127 StateMachine::update_range(&mut self.amount_number, self.position)
129 States::ParseAmountSiPrefix => {
130 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
136 self.state = next_state;
140 fn is_final(&self) -> bool {
141 self.state.is_final()
144 fn currency_prefix(&self) -> &Option<Range<usize>> {
145 &self.currency_prefix
148 fn amount_number(&self) -> &Option<Range<usize>> {
152 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
153 &self.amount_si_prefix
157 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::ParseError> {
158 let mut sm = StateMachine::new();
159 for c in input.chars() {
164 return Err(super::ParseError::MalformedHRP);
167 let currency = sm.currency_prefix().clone()
168 .map(|r| &input[r]).unwrap_or("");
169 let amount = sm.amount_number().clone()
170 .map(|r| &input[r]).unwrap_or("");
171 let si = sm.amount_si_prefix().clone()
172 .map(|r| &input[r]).unwrap_or("");
174 Ok((currency, amount, si))
179 impl FromStr for super::Currency {
180 type Err = ParseError;
182 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
183 match currency_prefix {
184 "bc" => Ok(Currency::Bitcoin),
185 "tb" => Ok(Currency::BitcoinTestnet),
186 "bcrt" => Ok(Currency::Regtest),
187 "sb" => Ok(Currency::Simnet),
188 "tbs" => Ok(Currency::Signet),
189 _ => Err(ParseError::UnknownCurrency)
194 impl FromStr for SiPrefix {
195 type Err = ParseError;
197 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
199 match currency_prefix {
204 _ => Err(ParseError::UnknownSiPrefix)
210 /// use lightning_invoice::Invoice;
212 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
213 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
214 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
217 /// assert!(invoice.parse::<Invoice>().is_ok());
219 impl FromStr for Invoice {
220 type Err = ParseOrSemanticError;
222 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
223 let signed = s.parse::<SignedRawInvoice>()?;
224 Ok(Invoice::from_signed(signed)?)
229 /// use lightning_invoice::*;
231 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
232 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
233 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
236 /// let parsed_1 = invoice.parse::<Invoice>();
238 /// let parsed_2 = match invoice.parse::<SignedRawInvoice>() {
239 /// Ok(signed) => match Invoice::from_signed(signed) {
240 /// Ok(invoice) => Ok(invoice),
241 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
243 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
246 /// assert!(parsed_1.is_ok());
247 /// assert_eq!(parsed_1, parsed_2);
249 impl FromStr for SignedRawInvoice {
250 type Err = ParseError;
252 fn from_str(s: &str) -> Result<Self, Self::Err> {
253 let (hrp, data, var) = bech32::decode(s)?;
255 if var == bech32::Variant::Bech32m {
256 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
257 // we didn't support Bech32m (which lightning does not use).
258 return Err(ParseError::Bech32Error(bech32::Error::InvalidChecksum));
261 if data.len() < 104 {
262 return Err(ParseError::TooShortDataPart);
265 let raw_hrp: RawHrp = hrp.parse()?;
266 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
268 Ok(SignedRawInvoice {
269 raw_invoice: RawInvoice {
273 hash: RawInvoice::hash_from_parts(
275 &data[..data.len()-104]
277 signature: InvoiceSignature::from_base32(&data[data.len()-104..])?,
282 impl FromStr for RawHrp {
283 type Err = ParseError;
285 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
286 let parts = parse_hrp(hrp)?;
288 let currency = parts.0.parse::<Currency>()?;
290 let amount = if !parts.1.is_empty() {
291 Some(parts.1.parse::<u64>()?)
296 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
299 let si: SiPrefix = parts.2.parse()?;
300 if let Some(amt) = amount {
301 if amt.checked_mul(si.multiplier()).is_none() {
302 return Err(ParseError::IntegerOverflowError);
311 si_prefix: si_prefix,
316 impl FromBase32 for RawDataPart {
317 type Err = ParseError;
319 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
320 if data.len() < 7 { // timestamp length
321 return Err(ParseError::TooShortDataPart);
324 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
325 let tagged = parse_tagged_parts(&data[7..])?;
328 timestamp: timestamp,
329 tagged_fields: tagged,
334 impl FromBase32 for PositiveTimestamp {
335 type Err = ParseError;
337 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
339 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
341 let timestamp: u64 = parse_int_be(b32, 32)
342 .expect("7*5bit < 64bit, no overflow possible");
343 match PositiveTimestamp::from_unix_timestamp(timestamp) {
345 Err(CreationError::TimestampOutOfBounds) => Err(ParseError::TimestampOverflow),
346 Err(_) => unreachable!(),
351 impl FromBase32 for InvoiceSignature {
352 type Err = ParseError;
353 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
354 if signature.len() != 104 {
355 return Err(ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into()));
357 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
358 let signature = &recoverable_signature_bytes[0..64];
359 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
361 Ok(InvoiceSignature(RecoverableSignature::from_compact(
368 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
369 where T: CheckedAdd + CheckedMul + From<u8> + Default,
372 digits.iter().fold(Some(Default::default()), |acc, b|
374 .and_then(|x| x.checked_mul(&base))
375 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
379 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
380 let mut parts = Vec::<RawTaggedField>::new();
383 while !data.is_empty() {
385 return Err(ParseError::UnexpectedEndOfTaggedFields);
388 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
389 // parse the length to find the end of the tagged field's data
390 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
391 let last_element = 3 + len;
393 if data.len() < last_element {
394 return Err(ParseError::UnexpectedEndOfTaggedFields);
397 // Get the tagged field's data slice
398 let field = &data[0..last_element];
400 // Set data slice to remaining data
401 data = &data[last_element..];
403 match TaggedField::from_base32(field) {
405 parts.push(RawTaggedField::KnownSemantics(field))
407 Err(ParseError::Skip) => {
408 parts.push(RawTaggedField::UnknownSemantics(field.into()))
410 Err(e) => {return Err(e)}
416 impl FromBase32 for TaggedField {
417 type Err = ParseError;
419 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
421 return Err(ParseError::UnexpectedEndOfTaggedFields);
425 let field_data = &field[3..];
428 constants::TAG_PAYMENT_HASH =>
429 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
430 constants::TAG_DESCRIPTION =>
431 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
432 constants::TAG_PAYEE_PUB_KEY =>
433 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
434 constants::TAG_DESCRIPTION_HASH =>
435 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
436 constants::TAG_EXPIRY_TIME =>
437 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
438 constants::TAG_MIN_FINAL_CLTV_EXPIRY =>
439 Ok(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry::from_base32(field_data)?)),
440 constants::TAG_FALLBACK =>
441 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
442 constants::TAG_PRIVATE_ROUTE =>
443 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
444 constants::TAG_PAYMENT_SECRET =>
445 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
446 constants::TAG_FEATURES =>
447 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
449 // "A reader MUST skip over unknown fields"
450 Err(ParseError::Skip)
456 impl FromBase32 for Sha256 {
457 type Err = ParseError;
459 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
460 if field_data.len() != 52 {
461 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
462 Err(ParseError::Skip)
464 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
465 .expect("length was checked before (52 u5 -> 32 u8)")))
470 impl FromBase32 for Description {
471 type Err = ParseError;
473 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
474 let bytes = Vec::<u8>::from_base32(field_data)?;
475 let description = String::from(str::from_utf8(&bytes)?);
476 Ok(Description::new(description).expect(
477 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
482 impl FromBase32 for PayeePubKey {
483 type Err = ParseError;
485 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
486 if field_data.len() != 53 {
487 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
488 Err(ParseError::Skip)
490 let data_bytes = Vec::<u8>::from_base32(field_data)?;
491 let pub_key = PublicKey::from_slice(&data_bytes)?;
497 impl FromBase32 for ExpiryTime {
498 type Err = ParseError;
500 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
501 match parse_int_be::<u64, u5>(field_data, 32)
502 .and_then(|t| ExpiryTime::from_seconds(t).ok()) // ok, since the only error is out of bounds
505 None => Err(ParseError::IntegerOverflowError),
510 impl FromBase32 for MinFinalCltvExpiry {
511 type Err = ParseError;
513 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiry, ParseError> {
514 let expiry = parse_int_be::<u64, u5>(field_data, 32);
515 if let Some(expiry) = expiry {
516 Ok(MinFinalCltvExpiry(expiry))
518 Err(ParseError::IntegerOverflowError)
523 impl FromBase32 for Fallback {
524 type Err = ParseError;
526 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
527 if field_data.len() < 1 {
528 return Err(ParseError::UnexpectedEndOfTaggedFields);
531 let version = field_data[0];
532 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
534 match version.to_u8() {
536 if bytes.len() < 2 || bytes.len() > 40 {
537 return Err(ParseError::InvalidSegWitProgramLength);
540 Ok(Fallback::SegWitProgram {
546 if bytes.len() != 20 {
547 return Err(ParseError::InvalidPubKeyHashLength);
549 //TODO: refactor once const generics are available
550 let mut pkh = [0u8; 20];
551 pkh.copy_from_slice(&bytes);
552 Ok(Fallback::PubKeyHash(pkh))
555 if bytes.len() != 20 {
556 return Err(ParseError::InvalidScriptHashLength);
558 let mut sh = [0u8; 20];
559 sh.copy_from_slice(&bytes);
560 Ok(Fallback::ScriptHash(sh))
562 _ => Err(ParseError::Skip)
567 impl FromBase32 for PrivateRoute {
568 type Err = ParseError;
570 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, ParseError> {
571 let bytes = Vec::<u8>::from_base32(field_data)?;
573 if bytes.len() % 51 != 0 {
574 return Err(ParseError::UnexpectedEndOfTaggedFields);
577 let mut route_hops = Vec::<RouteHintHop>::new();
579 let mut bytes = bytes.as_slice();
580 while !bytes.is_empty() {
581 let hop_bytes = &bytes[0..51];
582 bytes = &bytes[51..];
584 let mut channel_id: [u8; 8] = Default::default();
585 channel_id.copy_from_slice(&hop_bytes[33..41]);
587 let hop = RouteHintHop {
588 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
589 short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
591 base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
592 proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
594 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
595 htlc_minimum_msat: None,
596 htlc_maximum_msat: None,
599 route_hops.push(hop);
602 Ok(PrivateRoute(RouteHint(route_hops)))
606 /// Errors that indicate what is wrong with the invoice. They have some granularity for debug
607 /// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
608 #[allow(missing_docs)]
609 #[derive(PartialEq, Debug, Clone)]
610 pub enum ParseError {
611 Bech32Error(bech32::Error),
612 ParseAmountError(ParseIntError),
613 MalformedSignature(secp256k1::Error),
619 UnexpectedEndOfTaggedFields,
620 DescriptionDecodeError(str::Utf8Error),
622 IntegerOverflowError,
623 InvalidSegWitProgramLength,
624 InvalidPubKeyHashLength,
625 InvalidScriptHashLength,
627 InvalidSliceLength(String),
629 /// Not an error, but used internally to signal that a part of the invoice should be ignored
630 /// according to BOLT11
635 /// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
636 /// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
637 /// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
638 #[derive(PartialEq, Debug, Clone)]
639 pub enum ParseOrSemanticError {
640 /// The invoice couldn't be decoded
641 ParseError(ParseError),
643 /// The invoice could be decoded but violates the BOLT11 standard
644 SemanticError(::SemanticError),
647 impl Display for ParseError {
648 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
650 // TODO: find a way to combine the first three arms (e as error::Error?)
651 ParseError::Bech32Error(ref e) => {
652 write!(f, "Invalid bech32: {}", e)
654 ParseError::ParseAmountError(ref e) => {
655 write!(f, "Invalid amount in hrp ({})", e)
657 ParseError::MalformedSignature(ref e) => {
658 write!(f, "Invalid secp256k1 signature: {}", e)
660 ParseError::DescriptionDecodeError(ref e) => {
661 write!(f, "Description is not a valid utf-8 string: {}", e)
663 ParseError::InvalidSliceLength(ref function) => {
664 write!(f, "Slice in function {} had the wrong length", function)
666 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
667 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
668 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
669 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
670 ParseError::TooShortDataPart => {
671 f.write_str("data part too short (should be at least 111 bech32 chars long)")
673 ParseError::UnexpectedEndOfTaggedFields => {
674 f.write_str("tagged fields part ended unexpectedly")
676 ParseError::PaddingError => f.write_str("some data field had bad padding"),
677 ParseError::IntegerOverflowError => {
678 f.write_str("parsed integer doesn't fit into receiving type")
680 ParseError::InvalidSegWitProgramLength => {
681 f.write_str("fallback SegWit program is too long or too short")
683 ParseError::InvalidPubKeyHashLength => {
684 f.write_str("fallback public key hash has a length unequal 20 bytes")
686 ParseError::InvalidScriptHashLength => {
687 f.write_str("fallback script hash has a length unequal 32 bytes")
689 ParseError::InvalidRecoveryId => {
690 f.write_str("recovery id is out of range (should be in [0,3])")
692 ParseError::Skip => {
693 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
695 ParseError::TimestampOverflow => {
696 f.write_str("the invoice's timestamp could not be represented as SystemTime")
702 impl Display for ParseOrSemanticError {
703 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
705 ParseOrSemanticError::ParseError(err) => err.fmt(f),
706 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
711 impl error::Error for ParseError {}
713 impl error::Error for ParseOrSemanticError {}
715 macro_rules! from_error {
716 ($my_error:expr, $extern_error:ty) => {
717 impl From<$extern_error> for ParseError {
718 fn from(e: $extern_error) -> Self {
725 from_error!(ParseError::MalformedSignature, secp256k1::Error);
726 from_error!(ParseError::ParseAmountError, ParseIntError);
727 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
729 impl From<bech32::Error> for ParseError {
730 fn from(e: bech32::Error) -> Self {
732 bech32::Error::InvalidPadding => ParseError::PaddingError,
733 _ => ParseError::Bech32Error(e)
738 impl From<ParseError> for ParseOrSemanticError {
739 fn from(e: ParseError) -> Self {
740 ParseOrSemanticError::ParseError(e)
744 impl From<::SemanticError> for ParseOrSemanticError {
745 fn from(e: SemanticError) -> Self {
746 ParseOrSemanticError::SemanticError(e)
753 use secp256k1::PublicKey;
755 use bitcoin_hashes::hex::FromHex;
756 use bitcoin_hashes::sha256;
758 const CHARSET_REV: [i8; 128] = [
759 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
760 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
761 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
762 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
763 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
764 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
765 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
766 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
769 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
772 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
777 fn test_parse_currency_prefix() {
780 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
781 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
782 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
783 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
784 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
785 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
789 fn test_parse_int_from_bytes_be() {
790 use de::parse_int_be;
792 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
793 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
794 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
795 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
799 fn test_parse_sha256_hash() {
801 use bech32::FromBase32;
803 let input = from_bech32(
804 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
807 let hash = sha256::Hash::from_hex(
808 "0001020304050607080900010203040506070809000102030405060708090102"
810 let expected = Ok(Sha256(hash));
812 assert_eq!(Sha256::from_base32(&input), expected);
814 // make sure hashes of unknown length get skipped
815 let input_unexpected_length = from_bech32(
816 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
818 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
822 fn test_parse_description() {
824 use bech32::FromBase32;
826 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
827 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
828 assert_eq!(Description::from_base32(&input), expected);
832 fn test_parse_payee_pub_key() {
834 use bech32::FromBase32;
836 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
838 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
839 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
840 0x0f, 0x93, 0x4d, 0xd9, 0xad
842 let expected = Ok(PayeePubKey(
843 PublicKey::from_slice(&pk_bytes[..]).unwrap()
846 assert_eq!(PayeePubKey::from_base32(&input), expected);
849 let input_unexpected_length = from_bech32(
850 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
852 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
856 fn test_parse_expiry_time() {
858 use bech32::FromBase32;
860 let input = from_bech32("pu".as_bytes());
861 let expected = Ok(ExpiryTime::from_seconds(60).unwrap());
862 assert_eq!(ExpiryTime::from_base32(&input), expected);
864 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
865 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
869 fn test_parse_min_final_cltv_expiry() {
870 use ::MinFinalCltvExpiry;
871 use bech32::FromBase32;
873 let input = from_bech32("pr".as_bytes());
874 let expected = Ok(MinFinalCltvExpiry(35));
876 assert_eq!(MinFinalCltvExpiry::from_base32(&input), expected);
880 fn test_parse_fallback() {
882 use bech32::FromBase32;
886 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
887 Ok(Fallback::PubKeyHash([
888 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
889 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
893 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
894 Ok(Fallback::ScriptHash([
895 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
896 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
900 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
901 Ok(Fallback::SegWitProgram {
902 version: u5::try_from_u8(0).unwrap(),
903 program: Vec::from(&[
904 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
905 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
910 vec![u5::try_from_u8(21).unwrap(); 41],
911 Err(ParseError::Skip)
915 Err(ParseError::UnexpectedEndOfTaggedFields)
918 vec![u5::try_from_u8(1).unwrap(); 81],
919 Err(ParseError::InvalidSegWitProgramLength)
922 vec![u5::try_from_u8(17).unwrap(); 1],
923 Err(ParseError::InvalidPubKeyHashLength)
926 vec![u5::try_from_u8(18).unwrap(); 1],
927 Err(ParseError::InvalidScriptHashLength)
931 for (input, expected) in cases.into_iter() {
932 assert_eq!(Fallback::from_base32(&input), expected);
937 fn test_parse_route() {
938 use lightning::routing::network_graph::RoutingFees;
939 use lightning::routing::router::{RouteHint, RouteHintHop};
941 use bech32::FromBase32;
942 use de::parse_int_be;
944 let input = from_bech32(
945 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
946 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
949 let mut expected = Vec::<RouteHintHop>::new();
950 expected.push(RouteHintHop {
951 src_node_id: PublicKey::from_slice(
953 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
954 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
955 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
958 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
961 proportional_millionths: 20,
963 cltv_expiry_delta: 3,
964 htlc_minimum_msat: None,
965 htlc_maximum_msat: None
967 expected.push(RouteHintHop {
968 src_node_id: PublicKey::from_slice(
970 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
971 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
972 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
975 short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
978 proportional_millionths: 30,
980 cltv_expiry_delta: 4,
981 htlc_minimum_msat: None,
982 htlc_maximum_msat: None
985 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
988 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
989 Err(ParseError::UnexpectedEndOfTaggedFields)
994 fn test_payment_secret_and_features_de_and_ser() {
995 use lightning::ln::features::InvoiceFeatures;
996 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
998 use {SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
999 Currency, Sha256, PositiveTimestamp};
1001 // Feature bits 9, 15, and 99 are set.
1002 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
1003 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
1004 let invoice = SignedRawInvoice {
1005 raw_invoice: RawInvoice {
1007 currency: Currency::Bitcoin,
1008 raw_amount: Some(25),
1009 si_prefix: Some(SiPrefix::Milli)
1012 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1013 tagged_fields: vec ! [
1014 PaymentHash(Sha256(sha256::Hash::from_hex(
1015 "0001020304050607080900010203040506070809000102030405060708090102"
1016 ).unwrap())).into(),
1017 Description(::Description::new("coffee beans".to_owned()).unwrap()).into(),
1018 PaymentSecret(::PaymentSecret([17; 32])).into(),
1019 Features(expected_features).into()]}
1021 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1022 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1023 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1024 signature: InvoiceSignature(RecoverableSignature::from_compact(
1025 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1026 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1027 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1028 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1029 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1030 RecoveryId::from_i32(1).unwrap()
1033 assert_eq!(invoice_str, invoice.to_string());
1035 invoice_str.parse(),
1041 fn test_raw_signed_invoice_deserialization() {
1043 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1044 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1048 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1049 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1050 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1051 Ok(SignedRawInvoice {
1052 raw_invoice: RawInvoice {
1054 currency: Currency::Bitcoin,
1059 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1060 tagged_fields: vec ! [
1061 PaymentHash(Sha256(sha256::Hash::from_hex(
1062 "0001020304050607080900010203040506070809000102030405060708090102"
1063 ).unwrap())).into(),
1066 "Please consider supporting this project".to_owned()
1073 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1074 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1075 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1077 signature: InvoiceSignature(RecoverableSignature::from_compact(
1079 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1080 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1081 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1082 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1083 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1084 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1086 RecoveryId::from_i32(0).unwrap()