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;
14 use num_traits::{CheckedAdd, CheckedMul};
17 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
18 use secp256k1::key::PublicKey;
22 use self::hrp_sm::parse_hrp;
24 /// State machine to parse the hrp
28 #[derive(PartialEq, Eq, Debug)]
39 fn next_state(&self, read_symbol: char) -> Result<States, super::ParseError> {
42 if read_symbol == 'l' {
45 Err(super::ParseError::MalformedHRP)
49 if read_symbol == 'n' {
52 Err(super::ParseError::MalformedHRP)
56 if !read_symbol.is_numeric() {
57 Ok(States::ParseCurrencyPrefix)
59 Ok(States::ParseAmountNumber)
62 States::ParseCurrencyPrefix => {
63 if !read_symbol.is_numeric() {
64 Ok(States::ParseCurrencyPrefix)
66 Ok(States::ParseAmountNumber)
69 States::ParseAmountNumber => {
70 if read_symbol.is_numeric() {
71 Ok(States::ParseAmountNumber)
72 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
73 Ok(States::ParseAmountSiPrefix)
75 Err(super::ParseError::MalformedHRP)
78 States::ParseAmountSiPrefix => Err(super::ParseError::MalformedHRP),
82 fn is_final(&self) -> bool {
83 !(*self == States::ParseL || *self == States::ParseN)
91 currency_prefix: Option<Range<usize>>,
92 amount_number: Option<Range<usize>>,
93 amount_si_prefix: Option<Range<usize>>,
97 fn new() -> StateMachine {
101 currency_prefix: None,
103 amount_si_prefix: None,
107 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
108 let new_range = match *range {
109 None => Range {start: position, end: position + 1},
110 Some(ref r) => Range {start: r.start, end: r.end + 1},
112 *range = Some(new_range);
115 fn step(&mut self, c: char) -> Result<(), super::ParseError> {
116 let next_state = self.state.next_state(c)?;
118 States::ParseCurrencyPrefix => {
119 StateMachine::update_range(&mut self.currency_prefix, self.position)
121 States::ParseAmountNumber => {
122 StateMachine::update_range(&mut self.amount_number, self.position)
124 States::ParseAmountSiPrefix => {
125 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
131 self.state = next_state;
135 fn is_final(&self) -> bool {
136 self.state.is_final()
139 fn currency_prefix(&self) -> &Option<Range<usize>> {
140 &self.currency_prefix
143 fn amount_number(&self) -> &Option<Range<usize>> {
147 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
148 &self.amount_si_prefix
152 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::ParseError> {
153 let mut sm = StateMachine::new();
154 for c in input.chars() {
159 return Err(super::ParseError::MalformedHRP);
162 let currency = sm.currency_prefix().clone()
163 .map(|r| &input[r]).unwrap_or("");
164 let amount = sm.amount_number().clone()
165 .map(|r| &input[r]).unwrap_or("");
166 let si = sm.amount_si_prefix().clone()
167 .map(|r| &input[r]).unwrap_or("");
169 Ok((currency, amount, si))
174 impl FromStr for super::Currency {
175 type Err = ParseError;
177 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
178 match currency_prefix {
179 "bc" => Ok(Currency::Bitcoin),
180 "tb" => Ok(Currency::BitcoinTestnet),
181 "bcrt" => Ok(Currency::Regtest),
182 "sb" => Ok(Currency::Simnet),
183 _ => Err(ParseError::UnknownCurrency)
188 impl FromStr for SiPrefix {
189 type Err = ParseError;
191 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
193 match currency_prefix {
198 _ => Err(ParseError::UnknownSiPrefix)
204 /// use lightning_invoice::Invoice;
206 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
207 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
208 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
211 /// assert!(invoice.parse::<Invoice>().is_ok());
213 impl FromStr for Invoice {
214 type Err = ParseOrSemanticError;
216 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
217 let signed = s.parse::<SignedRawInvoice>()?;
218 Ok(Invoice::from_signed(signed)?)
223 /// use lightning_invoice::*;
225 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
226 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
227 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
230 /// let parsed_1 = invoice.parse::<Invoice>();
232 /// let parsed_2 = match invoice.parse::<SignedRawInvoice>() {
233 /// Ok(signed) => match Invoice::from_signed(signed) {
234 /// Ok(invoice) => Ok(invoice),
235 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
237 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
240 /// assert!(parsed_1.is_ok());
241 /// assert_eq!(parsed_1, parsed_2);
243 impl FromStr for SignedRawInvoice {
244 type Err = ParseError;
246 fn from_str(s: &str) -> Result<Self, Self::Err> {
247 let (hrp, data) = bech32::decode(s)?;
249 if data.len() < 104 {
250 return Err(ParseError::TooShortDataPart);
253 let raw_hrp: RawHrp = hrp.parse()?;
254 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
256 Ok(SignedRawInvoice {
257 raw_invoice: RawInvoice {
261 hash: RawInvoice::hash_from_parts(
263 &data[..data.len()-104]
265 signature: Signature::from_base32(&data[data.len()-104..])?,
270 impl FromStr for RawHrp {
271 type Err = ParseError;
273 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
274 let parts = parse_hrp(hrp)?;
276 let currency = parts.0.parse::<Currency>()?;
278 let amount = if !parts.1.is_empty() {
279 Some(parts.1.parse::<u64>()?)
284 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
287 let si: SiPrefix = parts.2.parse()?;
288 if let Some(amt) = amount {
289 if amt.checked_mul(si.multiplier()).is_none() {
290 return Err(ParseError::IntegerOverflowError);
299 si_prefix: si_prefix,
304 impl FromBase32 for RawDataPart {
305 type Err = ParseError;
307 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
308 if data.len() < 7 { // timestamp length
309 return Err(ParseError::TooShortDataPart);
312 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
313 let tagged = parse_tagged_parts(&data[7..])?;
316 timestamp: timestamp,
317 tagged_fields: tagged,
322 impl FromBase32 for PositiveTimestamp {
323 type Err = ParseError;
325 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
327 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
329 let timestamp: u64 = parse_int_be(b32, 32)
330 .expect("7*5bit < 64bit, no overflow possible");
331 match PositiveTimestamp::from_unix_timestamp(timestamp) {
333 Err(CreationError::TimestampOutOfBounds) => Err(ParseError::TimestampOverflow),
334 Err(_) => unreachable!(),
339 impl FromBase32 for Signature {
340 type Err = ParseError;
341 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
342 if signature.len() != 104 {
343 return Err(ParseError::InvalidSliceLength("Signature::from_base32()".into()));
345 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
346 let signature = &recoverable_signature_bytes[0..64];
347 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
349 Ok(Signature(RecoverableSignature::from_compact(
356 fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
357 where T: CheckedAdd + CheckedMul + From<u8> + Default,
360 digits.iter().fold(Some(Default::default()), |acc, b|
362 .and_then(|x| x.checked_mul(&base))
363 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
367 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
368 let mut parts = Vec::<RawTaggedField>::new();
371 while !data.is_empty() {
373 return Err(ParseError::UnexpectedEndOfTaggedFields);
376 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
377 // parse the length to find the end of the tagged field's data
378 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
379 let last_element = 3 + len;
381 if data.len() < last_element {
382 return Err(ParseError::UnexpectedEndOfTaggedFields);
385 // Get the tagged field's data slice
386 let field = &data[0..last_element];
388 // Set data slice to remaining data
389 data = &data[last_element..];
391 match TaggedField::from_base32(field) {
393 parts.push(RawTaggedField::KnownSemantics(field))
395 Err(ParseError::Skip) => {
396 parts.push(RawTaggedField::UnknownSemantics(field.into()))
398 Err(e) => {return Err(e)}
404 impl FromBase32 for TaggedField {
405 type Err = ParseError;
407 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
409 return Err(ParseError::UnexpectedEndOfTaggedFields);
413 let field_data = &field[3..];
416 constants::TAG_PAYMENT_HASH =>
417 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
418 constants::TAG_DESCRIPTION =>
419 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
420 constants::TAG_PAYEE_PUB_KEY =>
421 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
422 constants::TAG_DESCRIPTION_HASH =>
423 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
424 constants::TAG_EXPIRY_TIME =>
425 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
426 constants::TAG_MIN_FINAL_CLTV_EXPIRY =>
427 Ok(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry::from_base32(field_data)?)),
428 constants::TAG_FALLBACK =>
429 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
430 constants::TAG_ROUTE =>
431 Ok(TaggedField::Route(Route::from_base32(field_data)?)),
432 constants::TAG_PAYMENT_SECRET =>
433 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
435 // "A reader MUST skip over unknown fields"
436 Err(ParseError::Skip)
442 impl FromBase32 for Sha256 {
443 type Err = ParseError;
445 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
446 if field_data.len() != 52 {
447 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
448 Err(ParseError::Skip)
450 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
451 .expect("length was checked before (52 u5 -> 32 u8)")))
456 impl FromBase32 for Description {
457 type Err = ParseError;
459 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
460 let bytes = Vec::<u8>::from_base32(field_data)?;
461 let description = String::from(str::from_utf8(&bytes)?);
462 Ok(Description::new(description).expect(
463 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
468 impl FromBase32 for PayeePubKey {
469 type Err = ParseError;
471 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
472 if field_data.len() != 53 {
473 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
474 Err(ParseError::Skip)
476 let data_bytes = Vec::<u8>::from_base32(field_data)?;
477 let pub_key = PublicKey::from_slice(&data_bytes)?;
483 impl FromBase32 for PaymentSecret {
484 type Err = ParseError;
486 fn from_base32(field_data: &[u5]) -> Result<PaymentSecret, ParseError> {
487 if field_data.len() != 52 {
488 Err(ParseError::Skip)
490 let data_bytes = Vec::<u8>::from_base32(field_data)?;
491 let mut payment_secret = [0; 32];
492 payment_secret.copy_from_slice(&data_bytes);
493 Ok(PaymentSecret(payment_secret))
498 impl FromBase32 for ExpiryTime {
499 type Err = ParseError;
501 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
502 match parse_int_be::<u64, u5>(field_data, 32)
503 .and_then(|t| ExpiryTime::from_seconds(t).ok()) // ok, since the only error is out of bounds
506 None => Err(ParseError::IntegerOverflowError),
511 impl FromBase32 for MinFinalCltvExpiry {
512 type Err = ParseError;
514 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiry, ParseError> {
515 let expiry = parse_int_be::<u64, u5>(field_data, 32);
516 if let Some(expiry) = expiry {
517 Ok(MinFinalCltvExpiry(expiry))
519 Err(ParseError::IntegerOverflowError)
524 impl FromBase32 for Fallback {
525 type Err = ParseError;
527 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
528 if field_data.len() < 1 {
529 return Err(ParseError::UnexpectedEndOfTaggedFields);
532 let version = field_data[0];
533 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
535 match version.to_u8() {
537 if bytes.len() < 2 || bytes.len() > 40 {
538 return Err(ParseError::InvalidSegWitProgramLength);
541 Ok(Fallback::SegWitProgram {
547 if bytes.len() != 20 {
548 return Err(ParseError::InvalidPubKeyHashLength);
550 //TODO: refactor once const generics are available
551 let mut pkh = [0u8; 20];
552 pkh.copy_from_slice(&bytes);
553 Ok(Fallback::PubKeyHash(pkh))
556 if bytes.len() != 20 {
557 return Err(ParseError::InvalidScriptHashLength);
559 let mut sh = [0u8; 20];
560 sh.copy_from_slice(&bytes);
561 Ok(Fallback::ScriptHash(sh))
563 _ => Err(ParseError::Skip)
568 impl FromBase32 for Route {
569 type Err = ParseError;
571 fn from_base32(field_data: &[u5]) -> Result<Route, ParseError> {
572 let bytes = Vec::<u8>::from_base32(field_data)?;
574 if bytes.len() % 51 != 0 {
575 return Err(ParseError::UnexpectedEndOfTaggedFields);
578 let mut route_hops = Vec::<RouteHop>::new();
580 let mut bytes = bytes.as_slice();
581 while !bytes.is_empty() {
582 let hop_bytes = &bytes[0..51];
583 bytes = &bytes[51..];
585 let mut channel_id: [u8; 8] = Default::default();
586 channel_id.copy_from_slice(&hop_bytes[33..41]);
589 pubkey: PublicKey::from_slice(&hop_bytes[0..33])?,
590 short_channel_id: channel_id,
591 fee_base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
592 fee_proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
593 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?")
596 route_hops.push(hop);
599 Ok(Route(route_hops))
603 /// Errors that indicate what is wrong with the invoice. They have some granularity for debug
604 /// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
605 #[allow(missing_docs)]
606 #[derive(PartialEq, Debug, Clone)]
607 pub enum ParseError {
608 Bech32Error(bech32::Error),
609 ParseAmountError(ParseIntError),
610 MalformedSignature(secp256k1::Error),
616 UnexpectedEndOfTaggedFields,
617 DescriptionDecodeError(str::Utf8Error),
619 IntegerOverflowError,
620 InvalidSegWitProgramLength,
621 InvalidPubKeyHashLength,
622 InvalidScriptHashLength,
624 InvalidSliceLength(String),
626 /// Not an error, but used internally to signal that a part of the invoice should be ignored
627 /// according to BOLT11
632 /// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
633 /// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
634 /// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
635 #[derive(PartialEq, Debug, Clone)]
636 pub enum ParseOrSemanticError {
637 /// The invoice couldn't be decoded
638 ParseError(ParseError),
640 /// The invoice could be decoded but violates the BOLT11 standard
641 SemanticError(::SemanticError),
644 impl Display for ParseError {
645 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
647 // TODO: find a way to combine the first three arms (e as error::Error?)
648 ParseError::Bech32Error(ref e) => {
649 write!(f, "Invalid bech32: {}", e)
651 ParseError::ParseAmountError(ref e) => {
652 write!(f, "Invalid amount in hrp ({})", e)
654 ParseError::MalformedSignature(ref e) => {
655 write!(f, "Invalid secp256k1 signature: {}", e)
657 ParseError::DescriptionDecodeError(ref e) => {
658 write!(f, "Description is not a valid utf-8 string: {}", e)
660 ParseError::InvalidSliceLength(ref function) => {
661 write!(f, "Slice in function {} had the wrong length", function)
663 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
664 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
665 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
666 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
667 ParseError::TooShortDataPart => {
668 f.write_str("data part too short (should be at least 111 bech32 chars long)")
670 ParseError::UnexpectedEndOfTaggedFields => {
671 f.write_str("tagged fields part ended unexpectedly")
673 ParseError::PaddingError => f.write_str("some data field had bad padding"),
674 ParseError::IntegerOverflowError => {
675 f.write_str("parsed integer doesn't fit into receiving type")
677 ParseError::InvalidSegWitProgramLength => {
678 f.write_str("fallback SegWit program is too long or too short")
680 ParseError::InvalidPubKeyHashLength => {
681 f.write_str("fallback public key hash has a length unequal 20 bytes")
683 ParseError::InvalidScriptHashLength => {
684 f.write_str("fallback script hash has a length unequal 32 bytes")
686 ParseError::InvalidRecoveryId => {
687 f.write_str("recovery id is out of range (should be in [0,3])")
689 ParseError::Skip => {
690 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
692 ParseError::TimestampOverflow => {
693 f.write_str("the invoice's timestamp could not be represented as SystemTime")
699 impl Display for ParseOrSemanticError {
700 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
702 ParseOrSemanticError::ParseError(err) => err.fmt(f),
703 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
708 impl error::Error for ParseError {}
710 impl error::Error for ParseOrSemanticError {}
712 macro_rules! from_error {
713 ($my_error:expr, $extern_error:ty) => {
714 impl From<$extern_error> for ParseError {
715 fn from(e: $extern_error) -> Self {
722 from_error!(ParseError::MalformedSignature, secp256k1::Error);
723 from_error!(ParseError::ParseAmountError, ParseIntError);
724 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
726 impl From<bech32::Error> for ParseError {
727 fn from(e: bech32::Error) -> Self {
729 bech32::Error::InvalidPadding => ParseError::PaddingError,
730 _ => ParseError::Bech32Error(e)
735 impl From<ParseError> for ParseOrSemanticError {
736 fn from(e: ParseError) -> Self {
737 ParseOrSemanticError::ParseError(e)
741 impl From<::SemanticError> for ParseOrSemanticError {
742 fn from(e: SemanticError) -> Self {
743 ParseOrSemanticError::SemanticError(e)
750 use secp256k1::PublicKey;
752 use bitcoin_hashes::hex::FromHex;
753 use bitcoin_hashes::sha256;
755 const CHARSET_REV: [i8; 128] = [
756 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
757 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
758 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
759 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
760 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
761 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
762 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
763 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
766 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
769 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
774 fn test_parse_currency_prefix() {
777 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
778 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
779 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
780 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
781 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
785 fn test_parse_int_from_bytes_be() {
786 use de::parse_int_be;
788 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
789 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
790 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
791 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
795 fn test_parse_sha256_hash() {
797 use bech32::FromBase32;
799 let input = from_bech32(
800 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
803 let hash = sha256::Hash::from_hex(
804 "0001020304050607080900010203040506070809000102030405060708090102"
806 let expected = Ok(Sha256(hash));
808 assert_eq!(Sha256::from_base32(&input), expected);
810 // make sure hashes of unknown length get skipped
811 let input_unexpected_length = from_bech32(
812 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
814 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
818 fn test_parse_description() {
820 use bech32::FromBase32;
822 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
823 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
824 assert_eq!(Description::from_base32(&input), expected);
828 fn test_parse_payee_pub_key() {
830 use bech32::FromBase32;
832 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
834 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
835 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
836 0x0f, 0x93, 0x4d, 0xd9, 0xad
838 let expected = Ok(PayeePubKey(
839 PublicKey::from_slice(&pk_bytes[..]).unwrap()
842 assert_eq!(PayeePubKey::from_base32(&input), expected);
845 let input_unexpected_length = from_bech32(
846 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
848 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
852 fn test_parse_expiry_time() {
854 use bech32::FromBase32;
856 let input = from_bech32("pu".as_bytes());
857 let expected = Ok(ExpiryTime::from_seconds(60).unwrap());
858 assert_eq!(ExpiryTime::from_base32(&input), expected);
860 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
861 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
865 fn test_parse_min_final_cltv_expiry() {
866 use ::MinFinalCltvExpiry;
867 use bech32::FromBase32;
869 let input = from_bech32("pr".as_bytes());
870 let expected = Ok(MinFinalCltvExpiry(35));
872 assert_eq!(MinFinalCltvExpiry::from_base32(&input), expected);
876 fn test_parse_fallback() {
878 use bech32::FromBase32;
882 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
883 Ok(Fallback::PubKeyHash([
884 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
885 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
889 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
890 Ok(Fallback::ScriptHash([
891 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
892 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
896 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
897 Ok(Fallback::SegWitProgram {
898 version: u5::try_from_u8(0).unwrap(),
899 program: Vec::from(&[
900 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
901 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
906 vec![u5::try_from_u8(21).unwrap(); 41],
907 Err(ParseError::Skip)
911 Err(ParseError::UnexpectedEndOfTaggedFields)
914 vec![u5::try_from_u8(1).unwrap(); 81],
915 Err(ParseError::InvalidSegWitProgramLength)
918 vec![u5::try_from_u8(17).unwrap(); 1],
919 Err(ParseError::InvalidPubKeyHashLength)
922 vec![u5::try_from_u8(18).unwrap(); 1],
923 Err(ParseError::InvalidScriptHashLength)
927 for (input, expected) in cases.into_iter() {
928 assert_eq!(Fallback::from_base32(&input), expected);
933 fn test_parse_route() {
936 use bech32::FromBase32;
938 let input = from_bech32(
939 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
940 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
943 let mut expected = Vec::<RouteHop>::new();
944 expected.push(RouteHop {
945 pubkey: 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: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
954 fee_proportional_millionths: 20,
957 expected.push(RouteHop {
958 pubkey: PublicKey::from_slice(
960 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
961 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
962 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
965 short_channel_id: [0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a],
967 fee_proportional_millionths: 30,
971 assert_eq!(Route::from_base32(&input), Ok(Route(expected)));
974 Route::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
975 Err(ParseError::UnexpectedEndOfTaggedFields)
980 fn test_payment_secret_deserialization() {
981 use bech32::CheckBase32;
982 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
984 use {SiPrefix, SignedRawInvoice, Signature, RawInvoice, RawTaggedField, RawHrp, RawDataPart,
985 Currency, Sha256, PositiveTimestamp};
987 assert_eq!( // BOLT 11 payment secret invoice. The unknown fields are invoice features.
988 "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu".parse(),
989 Ok(SignedRawInvoice {
990 raw_invoice: RawInvoice {
992 currency: Currency::Bitcoin,
993 raw_amount: Some(25),
994 si_prefix: Some(SiPrefix::Milli)
997 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
998 tagged_fields: vec ! [
999 PaymentHash(Sha256(sha256::Hash::from_hex(
1000 "0001020304050607080900010203040506070809000102030405060708090102"
1001 ).unwrap())).into(),
1002 Description(::Description::new("coffee beans".to_owned()).unwrap()).into(),
1003 PaymentSecret(::PaymentSecret([17; 32])).into(),
1004 RawTaggedField::UnknownSemantics(vec![5, 0, 20, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1005 0, 0, 0, 0, 1, 0, 16,
1006 0].check_base32().unwrap())],
1009 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1010 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1011 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1012 signature: Signature(RecoverableSignature::from_compact(
1013 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1014 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1015 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1016 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1017 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1018 RecoveryId::from_i32(1).unwrap()
1025 fn test_raw_signed_invoice_deserialization() {
1027 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1028 use {SignedRawInvoice, Signature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1032 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1033 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1034 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1035 Ok(SignedRawInvoice {
1036 raw_invoice: RawInvoice {
1038 currency: Currency::Bitcoin,
1043 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1044 tagged_fields: vec ! [
1045 PaymentHash(Sha256(sha256::Hash::from_hex(
1046 "0001020304050607080900010203040506070809000102030405060708090102"
1047 ).unwrap())).into(),
1050 "Please consider supporting this project".to_owned()
1057 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1058 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1059 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1061 signature: Signature(RecoverableSignature::from_compact(
1063 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1064 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1065 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1066 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1067 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1068 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1070 RecoveryId::from_i32(0).unwrap()