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) = bech32::decode(s)?;
255 if data.len() < 104 {
256 return Err(ParseError::TooShortDataPart);
259 let raw_hrp: RawHrp = hrp.parse()?;
260 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
262 Ok(SignedRawInvoice {
263 raw_invoice: RawInvoice {
267 hash: RawInvoice::hash_from_parts(
269 &data[..data.len()-104]
271 signature: InvoiceSignature::from_base32(&data[data.len()-104..])?,
276 impl FromStr for RawHrp {
277 type Err = ParseError;
279 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
280 let parts = parse_hrp(hrp)?;
282 let currency = parts.0.parse::<Currency>()?;
284 let amount = if !parts.1.is_empty() {
285 Some(parts.1.parse::<u64>()?)
290 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
293 let si: SiPrefix = parts.2.parse()?;
294 if let Some(amt) = amount {
295 if amt.checked_mul(si.multiplier()).is_none() {
296 return Err(ParseError::IntegerOverflowError);
305 si_prefix: si_prefix,
310 impl FromBase32 for RawDataPart {
311 type Err = ParseError;
313 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
314 if data.len() < 7 { // timestamp length
315 return Err(ParseError::TooShortDataPart);
318 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
319 let tagged = parse_tagged_parts(&data[7..])?;
322 timestamp: timestamp,
323 tagged_fields: tagged,
328 impl FromBase32 for PositiveTimestamp {
329 type Err = ParseError;
331 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
333 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
335 let timestamp: u64 = parse_int_be(b32, 32)
336 .expect("7*5bit < 64bit, no overflow possible");
337 match PositiveTimestamp::from_unix_timestamp(timestamp) {
339 Err(CreationError::TimestampOutOfBounds) => Err(ParseError::TimestampOverflow),
340 Err(_) => unreachable!(),
345 impl FromBase32 for InvoiceSignature {
346 type Err = ParseError;
347 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
348 if signature.len() != 104 {
349 return Err(ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into()));
351 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
352 let signature = &recoverable_signature_bytes[0..64];
353 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
355 Ok(InvoiceSignature(RecoverableSignature::from_compact(
362 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
363 where T: CheckedAdd + CheckedMul + From<u8> + Default,
366 digits.iter().fold(Some(Default::default()), |acc, b|
368 .and_then(|x| x.checked_mul(&base))
369 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
373 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
374 let mut parts = Vec::<RawTaggedField>::new();
377 while !data.is_empty() {
379 return Err(ParseError::UnexpectedEndOfTaggedFields);
382 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
383 // parse the length to find the end of the tagged field's data
384 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
385 let last_element = 3 + len;
387 if data.len() < last_element {
388 return Err(ParseError::UnexpectedEndOfTaggedFields);
391 // Get the tagged field's data slice
392 let field = &data[0..last_element];
394 // Set data slice to remaining data
395 data = &data[last_element..];
397 match TaggedField::from_base32(field) {
399 parts.push(RawTaggedField::KnownSemantics(field))
401 Err(ParseError::Skip) => {
402 parts.push(RawTaggedField::UnknownSemantics(field.into()))
404 Err(e) => {return Err(e)}
410 impl FromBase32 for TaggedField {
411 type Err = ParseError;
413 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
415 return Err(ParseError::UnexpectedEndOfTaggedFields);
419 let field_data = &field[3..];
422 constants::TAG_PAYMENT_HASH =>
423 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
424 constants::TAG_DESCRIPTION =>
425 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
426 constants::TAG_PAYEE_PUB_KEY =>
427 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
428 constants::TAG_DESCRIPTION_HASH =>
429 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
430 constants::TAG_EXPIRY_TIME =>
431 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
432 constants::TAG_MIN_FINAL_CLTV_EXPIRY =>
433 Ok(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry::from_base32(field_data)?)),
434 constants::TAG_FALLBACK =>
435 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
436 constants::TAG_PRIVATE_ROUTE =>
437 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
438 constants::TAG_PAYMENT_SECRET =>
439 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
440 constants::TAG_FEATURES =>
441 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
443 // "A reader MUST skip over unknown fields"
444 Err(ParseError::Skip)
450 impl FromBase32 for Sha256 {
451 type Err = ParseError;
453 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
454 if field_data.len() != 52 {
455 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
456 Err(ParseError::Skip)
458 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
459 .expect("length was checked before (52 u5 -> 32 u8)")))
464 impl FromBase32 for Description {
465 type Err = ParseError;
467 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
468 let bytes = Vec::<u8>::from_base32(field_data)?;
469 let description = String::from(str::from_utf8(&bytes)?);
470 Ok(Description::new(description).expect(
471 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
476 impl FromBase32 for PayeePubKey {
477 type Err = ParseError;
479 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
480 if field_data.len() != 53 {
481 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
482 Err(ParseError::Skip)
484 let data_bytes = Vec::<u8>::from_base32(field_data)?;
485 let pub_key = PublicKey::from_slice(&data_bytes)?;
491 impl FromBase32 for ExpiryTime {
492 type Err = ParseError;
494 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
495 match parse_int_be::<u64, u5>(field_data, 32)
496 .and_then(|t| ExpiryTime::from_seconds(t).ok()) // ok, since the only error is out of bounds
499 None => Err(ParseError::IntegerOverflowError),
504 impl FromBase32 for MinFinalCltvExpiry {
505 type Err = ParseError;
507 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiry, ParseError> {
508 let expiry = parse_int_be::<u64, u5>(field_data, 32);
509 if let Some(expiry) = expiry {
510 Ok(MinFinalCltvExpiry(expiry))
512 Err(ParseError::IntegerOverflowError)
517 impl FromBase32 for Fallback {
518 type Err = ParseError;
520 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
521 if field_data.len() < 1 {
522 return Err(ParseError::UnexpectedEndOfTaggedFields);
525 let version = field_data[0];
526 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
528 match version.to_u8() {
530 if bytes.len() < 2 || bytes.len() > 40 {
531 return Err(ParseError::InvalidSegWitProgramLength);
534 Ok(Fallback::SegWitProgram {
540 if bytes.len() != 20 {
541 return Err(ParseError::InvalidPubKeyHashLength);
543 //TODO: refactor once const generics are available
544 let mut pkh = [0u8; 20];
545 pkh.copy_from_slice(&bytes);
546 Ok(Fallback::PubKeyHash(pkh))
549 if bytes.len() != 20 {
550 return Err(ParseError::InvalidScriptHashLength);
552 let mut sh = [0u8; 20];
553 sh.copy_from_slice(&bytes);
554 Ok(Fallback::ScriptHash(sh))
556 _ => Err(ParseError::Skip)
561 impl FromBase32 for PrivateRoute {
562 type Err = ParseError;
564 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, ParseError> {
565 let bytes = Vec::<u8>::from_base32(field_data)?;
567 if bytes.len() % 51 != 0 {
568 return Err(ParseError::UnexpectedEndOfTaggedFields);
571 let mut route_hops = Vec::<RouteHintHop>::new();
573 let mut bytes = bytes.as_slice();
574 while !bytes.is_empty() {
575 let hop_bytes = &bytes[0..51];
576 bytes = &bytes[51..];
578 let mut channel_id: [u8; 8] = Default::default();
579 channel_id.copy_from_slice(&hop_bytes[33..41]);
581 let hop = RouteHintHop {
582 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
583 short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
585 base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
586 proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
588 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
589 htlc_minimum_msat: None,
590 htlc_maximum_msat: None,
593 route_hops.push(hop);
596 Ok(PrivateRoute(RouteHint(route_hops)))
600 /// Errors that indicate what is wrong with the invoice. They have some granularity for debug
601 /// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
602 #[allow(missing_docs)]
603 #[derive(PartialEq, Debug, Clone)]
604 pub enum ParseError {
605 Bech32Error(bech32::Error),
606 ParseAmountError(ParseIntError),
607 MalformedSignature(secp256k1::Error),
613 UnexpectedEndOfTaggedFields,
614 DescriptionDecodeError(str::Utf8Error),
616 IntegerOverflowError,
617 InvalidSegWitProgramLength,
618 InvalidPubKeyHashLength,
619 InvalidScriptHashLength,
621 InvalidSliceLength(String),
623 /// Not an error, but used internally to signal that a part of the invoice should be ignored
624 /// according to BOLT11
629 /// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
630 /// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
631 /// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
632 #[derive(PartialEq, Debug, Clone)]
633 pub enum ParseOrSemanticError {
634 /// The invoice couldn't be decoded
635 ParseError(ParseError),
637 /// The invoice could be decoded but violates the BOLT11 standard
638 SemanticError(::SemanticError),
641 impl Display for ParseError {
642 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
644 // TODO: find a way to combine the first three arms (e as error::Error?)
645 ParseError::Bech32Error(ref e) => {
646 write!(f, "Invalid bech32: {}", e)
648 ParseError::ParseAmountError(ref e) => {
649 write!(f, "Invalid amount in hrp ({})", e)
651 ParseError::MalformedSignature(ref e) => {
652 write!(f, "Invalid secp256k1 signature: {}", e)
654 ParseError::DescriptionDecodeError(ref e) => {
655 write!(f, "Description is not a valid utf-8 string: {}", e)
657 ParseError::InvalidSliceLength(ref function) => {
658 write!(f, "Slice in function {} had the wrong length", function)
660 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
661 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
662 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
663 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
664 ParseError::TooShortDataPart => {
665 f.write_str("data part too short (should be at least 111 bech32 chars long)")
667 ParseError::UnexpectedEndOfTaggedFields => {
668 f.write_str("tagged fields part ended unexpectedly")
670 ParseError::PaddingError => f.write_str("some data field had bad padding"),
671 ParseError::IntegerOverflowError => {
672 f.write_str("parsed integer doesn't fit into receiving type")
674 ParseError::InvalidSegWitProgramLength => {
675 f.write_str("fallback SegWit program is too long or too short")
677 ParseError::InvalidPubKeyHashLength => {
678 f.write_str("fallback public key hash has a length unequal 20 bytes")
680 ParseError::InvalidScriptHashLength => {
681 f.write_str("fallback script hash has a length unequal 32 bytes")
683 ParseError::InvalidRecoveryId => {
684 f.write_str("recovery id is out of range (should be in [0,3])")
686 ParseError::Skip => {
687 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
689 ParseError::TimestampOverflow => {
690 f.write_str("the invoice's timestamp could not be represented as SystemTime")
696 impl Display for ParseOrSemanticError {
697 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
699 ParseOrSemanticError::ParseError(err) => err.fmt(f),
700 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
705 impl error::Error for ParseError {}
707 impl error::Error for ParseOrSemanticError {}
709 macro_rules! from_error {
710 ($my_error:expr, $extern_error:ty) => {
711 impl From<$extern_error> for ParseError {
712 fn from(e: $extern_error) -> Self {
719 from_error!(ParseError::MalformedSignature, secp256k1::Error);
720 from_error!(ParseError::ParseAmountError, ParseIntError);
721 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
723 impl From<bech32::Error> for ParseError {
724 fn from(e: bech32::Error) -> Self {
726 bech32::Error::InvalidPadding => ParseError::PaddingError,
727 _ => ParseError::Bech32Error(e)
732 impl From<ParseError> for ParseOrSemanticError {
733 fn from(e: ParseError) -> Self {
734 ParseOrSemanticError::ParseError(e)
738 impl From<::SemanticError> for ParseOrSemanticError {
739 fn from(e: SemanticError) -> Self {
740 ParseOrSemanticError::SemanticError(e)
747 use secp256k1::PublicKey;
749 use bitcoin_hashes::hex::FromHex;
750 use bitcoin_hashes::sha256;
752 const CHARSET_REV: [i8; 128] = [
753 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
754 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
755 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
756 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
757 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
758 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
759 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
760 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
763 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
766 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
771 fn test_parse_currency_prefix() {
774 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
775 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
776 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
777 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
778 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
779 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
783 fn test_parse_int_from_bytes_be() {
784 use de::parse_int_be;
786 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
787 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
788 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
789 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
793 fn test_parse_sha256_hash() {
795 use bech32::FromBase32;
797 let input = from_bech32(
798 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
801 let hash = sha256::Hash::from_hex(
802 "0001020304050607080900010203040506070809000102030405060708090102"
804 let expected = Ok(Sha256(hash));
806 assert_eq!(Sha256::from_base32(&input), expected);
808 // make sure hashes of unknown length get skipped
809 let input_unexpected_length = from_bech32(
810 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
812 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
816 fn test_parse_description() {
818 use bech32::FromBase32;
820 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
821 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
822 assert_eq!(Description::from_base32(&input), expected);
826 fn test_parse_payee_pub_key() {
828 use bech32::FromBase32;
830 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
832 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
833 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
834 0x0f, 0x93, 0x4d, 0xd9, 0xad
836 let expected = Ok(PayeePubKey(
837 PublicKey::from_slice(&pk_bytes[..]).unwrap()
840 assert_eq!(PayeePubKey::from_base32(&input), expected);
843 let input_unexpected_length = from_bech32(
844 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
846 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
850 fn test_parse_expiry_time() {
852 use bech32::FromBase32;
854 let input = from_bech32("pu".as_bytes());
855 let expected = Ok(ExpiryTime::from_seconds(60).unwrap());
856 assert_eq!(ExpiryTime::from_base32(&input), expected);
858 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
859 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
863 fn test_parse_min_final_cltv_expiry() {
864 use ::MinFinalCltvExpiry;
865 use bech32::FromBase32;
867 let input = from_bech32("pr".as_bytes());
868 let expected = Ok(MinFinalCltvExpiry(35));
870 assert_eq!(MinFinalCltvExpiry::from_base32(&input), expected);
874 fn test_parse_fallback() {
876 use bech32::FromBase32;
880 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
881 Ok(Fallback::PubKeyHash([
882 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
883 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
887 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
888 Ok(Fallback::ScriptHash([
889 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
890 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
894 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
895 Ok(Fallback::SegWitProgram {
896 version: u5::try_from_u8(0).unwrap(),
897 program: Vec::from(&[
898 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
899 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
904 vec![u5::try_from_u8(21).unwrap(); 41],
905 Err(ParseError::Skip)
909 Err(ParseError::UnexpectedEndOfTaggedFields)
912 vec![u5::try_from_u8(1).unwrap(); 81],
913 Err(ParseError::InvalidSegWitProgramLength)
916 vec![u5::try_from_u8(17).unwrap(); 1],
917 Err(ParseError::InvalidPubKeyHashLength)
920 vec![u5::try_from_u8(18).unwrap(); 1],
921 Err(ParseError::InvalidScriptHashLength)
925 for (input, expected) in cases.into_iter() {
926 assert_eq!(Fallback::from_base32(&input), expected);
931 fn test_parse_route() {
932 use lightning::routing::network_graph::RoutingFees;
933 use lightning::routing::router::{RouteHint, RouteHintHop};
935 use bech32::FromBase32;
936 use de::parse_int_be;
938 let input = from_bech32(
939 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
940 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
943 let mut expected = Vec::<RouteHintHop>::new();
944 expected.push(RouteHintHop {
945 src_node_id: PublicKey::from_slice(
947 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
948 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
949 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
952 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
955 proportional_millionths: 20,
957 cltv_expiry_delta: 3,
958 htlc_minimum_msat: None,
959 htlc_maximum_msat: None
961 expected.push(RouteHintHop {
962 src_node_id: PublicKey::from_slice(
964 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
965 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
966 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
969 short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
972 proportional_millionths: 30,
974 cltv_expiry_delta: 4,
975 htlc_minimum_msat: None,
976 htlc_maximum_msat: None
979 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
982 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
983 Err(ParseError::UnexpectedEndOfTaggedFields)
988 fn test_payment_secret_and_features_de_and_ser() {
989 use lightning::ln::features::InvoiceFeatures;
990 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
992 use {SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
993 Currency, Sha256, PositiveTimestamp};
995 // Feature bits 9, 15, and 99 are set.
996 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
997 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
998 let invoice = SignedRawInvoice {
999 raw_invoice: RawInvoice {
1001 currency: Currency::Bitcoin,
1002 raw_amount: Some(25),
1003 si_prefix: Some(SiPrefix::Milli)
1006 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1007 tagged_fields: vec ! [
1008 PaymentHash(Sha256(sha256::Hash::from_hex(
1009 "0001020304050607080900010203040506070809000102030405060708090102"
1010 ).unwrap())).into(),
1011 Description(::Description::new("coffee beans".to_owned()).unwrap()).into(),
1012 PaymentSecret(::PaymentSecret([17; 32])).into(),
1013 Features(expected_features).into()]}
1015 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1016 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1017 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1018 signature: InvoiceSignature(RecoverableSignature::from_compact(
1019 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1020 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1021 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1022 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1023 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1024 RecoveryId::from_i32(1).unwrap()
1027 assert_eq!(invoice_str, invoice.to_string());
1029 invoice_str.parse(),
1035 fn test_raw_signed_invoice_deserialization() {
1037 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1038 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1042 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1043 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1044 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1045 Ok(SignedRawInvoice {
1046 raw_invoice: RawInvoice {
1048 currency: Currency::Bitcoin,
1053 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1054 tagged_fields: vec ! [
1055 PaymentHash(Sha256(sha256::Hash::from_hex(
1056 "0001020304050607080900010203040506070809000102030405060708090102"
1057 ).unwrap())).into(),
1060 "Please consider supporting this project".to_owned()
1067 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1068 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1069 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1071 signature: InvoiceSignature(RecoverableSignature::from_compact(
1073 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1074 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1075 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1076 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1077 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1078 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1080 RecoveryId::from_i32(0).unwrap()