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::RouteHintHop;
17 use num_traits::{CheckedAdd, CheckedMul};
20 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
21 use secp256k1::key::PublicKey;
25 use self::hrp_sm::parse_hrp;
27 /// State machine to parse the hrp
31 #[derive(PartialEq, Eq, Debug)]
42 fn next_state(&self, read_symbol: char) -> Result<States, super::ParseError> {
45 if read_symbol == 'l' {
48 Err(super::ParseError::MalformedHRP)
52 if read_symbol == 'n' {
55 Err(super::ParseError::MalformedHRP)
59 if !read_symbol.is_numeric() {
60 Ok(States::ParseCurrencyPrefix)
62 Ok(States::ParseAmountNumber)
65 States::ParseCurrencyPrefix => {
66 if !read_symbol.is_numeric() {
67 Ok(States::ParseCurrencyPrefix)
69 Ok(States::ParseAmountNumber)
72 States::ParseAmountNumber => {
73 if read_symbol.is_numeric() {
74 Ok(States::ParseAmountNumber)
75 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
76 Ok(States::ParseAmountSiPrefix)
78 Err(super::ParseError::MalformedHRP)
81 States::ParseAmountSiPrefix => Err(super::ParseError::MalformedHRP),
85 fn is_final(&self) -> bool {
86 !(*self == States::ParseL || *self == States::ParseN)
94 currency_prefix: Option<Range<usize>>,
95 amount_number: Option<Range<usize>>,
96 amount_si_prefix: Option<Range<usize>>,
100 fn new() -> StateMachine {
102 state: States::Start,
104 currency_prefix: None,
106 amount_si_prefix: None,
110 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
111 let new_range = match *range {
112 None => Range {start: position, end: position + 1},
113 Some(ref r) => Range {start: r.start, end: r.end + 1},
115 *range = Some(new_range);
118 fn step(&mut self, c: char) -> Result<(), super::ParseError> {
119 let next_state = self.state.next_state(c)?;
121 States::ParseCurrencyPrefix => {
122 StateMachine::update_range(&mut self.currency_prefix, self.position)
124 States::ParseAmountNumber => {
125 StateMachine::update_range(&mut self.amount_number, self.position)
127 States::ParseAmountSiPrefix => {
128 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
134 self.state = next_state;
138 fn is_final(&self) -> bool {
139 self.state.is_final()
142 fn currency_prefix(&self) -> &Option<Range<usize>> {
143 &self.currency_prefix
146 fn amount_number(&self) -> &Option<Range<usize>> {
150 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
151 &self.amount_si_prefix
155 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::ParseError> {
156 let mut sm = StateMachine::new();
157 for c in input.chars() {
162 return Err(super::ParseError::MalformedHRP);
165 let currency = sm.currency_prefix().clone()
166 .map(|r| &input[r]).unwrap_or("");
167 let amount = sm.amount_number().clone()
168 .map(|r| &input[r]).unwrap_or("");
169 let si = sm.amount_si_prefix().clone()
170 .map(|r| &input[r]).unwrap_or("");
172 Ok((currency, amount, si))
177 impl FromStr for super::Currency {
178 type Err = ParseError;
180 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
181 match currency_prefix {
182 "bc" => Ok(Currency::Bitcoin),
183 "tb" => Ok(Currency::BitcoinTestnet),
184 "bcrt" => Ok(Currency::Regtest),
185 "sb" => Ok(Currency::Simnet),
186 _ => Err(ParseError::UnknownCurrency)
191 impl FromStr for SiPrefix {
192 type Err = ParseError;
194 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
196 match currency_prefix {
201 _ => Err(ParseError::UnknownSiPrefix)
207 /// use lightning_invoice::Invoice;
209 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
210 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
211 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
214 /// assert!(invoice.parse::<Invoice>().is_ok());
216 impl FromStr for Invoice {
217 type Err = ParseOrSemanticError;
219 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
220 let signed = s.parse::<SignedRawInvoice>()?;
221 Ok(Invoice::from_signed(signed)?)
226 /// use lightning_invoice::*;
228 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
229 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
230 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
233 /// let parsed_1 = invoice.parse::<Invoice>();
235 /// let parsed_2 = match invoice.parse::<SignedRawInvoice>() {
236 /// Ok(signed) => match Invoice::from_signed(signed) {
237 /// Ok(invoice) => Ok(invoice),
238 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
240 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
243 /// assert!(parsed_1.is_ok());
244 /// assert_eq!(parsed_1, parsed_2);
246 impl FromStr for SignedRawInvoice {
247 type Err = ParseError;
249 fn from_str(s: &str) -> Result<Self, Self::Err> {
250 let (hrp, data) = bech32::decode(s)?;
252 if data.len() < 104 {
253 return Err(ParseError::TooShortDataPart);
256 let raw_hrp: RawHrp = hrp.parse()?;
257 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
259 Ok(SignedRawInvoice {
260 raw_invoice: RawInvoice {
264 hash: RawInvoice::hash_from_parts(
266 &data[..data.len()-104]
268 signature: InvoiceSignature::from_base32(&data[data.len()-104..])?,
273 impl FromStr for RawHrp {
274 type Err = ParseError;
276 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
277 let parts = parse_hrp(hrp)?;
279 let currency = parts.0.parse::<Currency>()?;
281 let amount = if !parts.1.is_empty() {
282 Some(parts.1.parse::<u64>()?)
287 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
290 let si: SiPrefix = parts.2.parse()?;
291 if let Some(amt) = amount {
292 if amt.checked_mul(si.multiplier()).is_none() {
293 return Err(ParseError::IntegerOverflowError);
302 si_prefix: si_prefix,
307 impl FromBase32 for RawDataPart {
308 type Err = ParseError;
310 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
311 if data.len() < 7 { // timestamp length
312 return Err(ParseError::TooShortDataPart);
315 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
316 let tagged = parse_tagged_parts(&data[7..])?;
319 timestamp: timestamp,
320 tagged_fields: tagged,
325 impl FromBase32 for PositiveTimestamp {
326 type Err = ParseError;
328 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
330 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
332 let timestamp: u64 = parse_int_be(b32, 32)
333 .expect("7*5bit < 64bit, no overflow possible");
334 match PositiveTimestamp::from_unix_timestamp(timestamp) {
336 Err(CreationError::TimestampOutOfBounds) => Err(ParseError::TimestampOverflow),
337 Err(_) => unreachable!(),
342 impl FromBase32 for InvoiceSignature {
343 type Err = ParseError;
344 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
345 if signature.len() != 104 {
346 return Err(ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into()));
348 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
349 let signature = &recoverable_signature_bytes[0..64];
350 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
352 Ok(InvoiceSignature(RecoverableSignature::from_compact(
359 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
360 where T: CheckedAdd + CheckedMul + From<u8> + Default,
363 digits.iter().fold(Some(Default::default()), |acc, b|
365 .and_then(|x| x.checked_mul(&base))
366 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
370 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
371 let mut parts = Vec::<RawTaggedField>::new();
374 while !data.is_empty() {
376 return Err(ParseError::UnexpectedEndOfTaggedFields);
379 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
380 // parse the length to find the end of the tagged field's data
381 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
382 let last_element = 3 + len;
384 if data.len() < last_element {
385 return Err(ParseError::UnexpectedEndOfTaggedFields);
388 // Get the tagged field's data slice
389 let field = &data[0..last_element];
391 // Set data slice to remaining data
392 data = &data[last_element..];
394 match TaggedField::from_base32(field) {
396 parts.push(RawTaggedField::KnownSemantics(field))
398 Err(ParseError::Skip) => {
399 parts.push(RawTaggedField::UnknownSemantics(field.into()))
401 Err(e) => {return Err(e)}
407 impl FromBase32 for TaggedField {
408 type Err = ParseError;
410 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
412 return Err(ParseError::UnexpectedEndOfTaggedFields);
416 let field_data = &field[3..];
419 constants::TAG_PAYMENT_HASH =>
420 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
421 constants::TAG_DESCRIPTION =>
422 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
423 constants::TAG_PAYEE_PUB_KEY =>
424 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
425 constants::TAG_DESCRIPTION_HASH =>
426 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
427 constants::TAG_EXPIRY_TIME =>
428 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
429 constants::TAG_MIN_FINAL_CLTV_EXPIRY =>
430 Ok(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry::from_base32(field_data)?)),
431 constants::TAG_FALLBACK =>
432 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
433 constants::TAG_ROUTE =>
434 Ok(TaggedField::Route(RouteHint::from_base32(field_data)?)),
435 constants::TAG_PAYMENT_SECRET =>
436 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
437 constants::TAG_FEATURES =>
438 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
440 // "A reader MUST skip over unknown fields"
441 Err(ParseError::Skip)
447 impl FromBase32 for Sha256 {
448 type Err = ParseError;
450 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
451 if field_data.len() != 52 {
452 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
453 Err(ParseError::Skip)
455 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
456 .expect("length was checked before (52 u5 -> 32 u8)")))
461 impl FromBase32 for Description {
462 type Err = ParseError;
464 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
465 let bytes = Vec::<u8>::from_base32(field_data)?;
466 let description = String::from(str::from_utf8(&bytes)?);
467 Ok(Description::new(description).expect(
468 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
473 impl FromBase32 for PayeePubKey {
474 type Err = ParseError;
476 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
477 if field_data.len() != 53 {
478 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
479 Err(ParseError::Skip)
481 let data_bytes = Vec::<u8>::from_base32(field_data)?;
482 let pub_key = PublicKey::from_slice(&data_bytes)?;
488 impl FromBase32 for ExpiryTime {
489 type Err = ParseError;
491 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
492 match parse_int_be::<u64, u5>(field_data, 32)
493 .and_then(|t| ExpiryTime::from_seconds(t).ok()) // ok, since the only error is out of bounds
496 None => Err(ParseError::IntegerOverflowError),
501 impl FromBase32 for MinFinalCltvExpiry {
502 type Err = ParseError;
504 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiry, ParseError> {
505 let expiry = parse_int_be::<u64, u5>(field_data, 32);
506 if let Some(expiry) = expiry {
507 Ok(MinFinalCltvExpiry(expiry))
509 Err(ParseError::IntegerOverflowError)
514 impl FromBase32 for Fallback {
515 type Err = ParseError;
517 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
518 if field_data.len() < 1 {
519 return Err(ParseError::UnexpectedEndOfTaggedFields);
522 let version = field_data[0];
523 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
525 match version.to_u8() {
527 if bytes.len() < 2 || bytes.len() > 40 {
528 return Err(ParseError::InvalidSegWitProgramLength);
531 Ok(Fallback::SegWitProgram {
537 if bytes.len() != 20 {
538 return Err(ParseError::InvalidPubKeyHashLength);
540 //TODO: refactor once const generics are available
541 let mut pkh = [0u8; 20];
542 pkh.copy_from_slice(&bytes);
543 Ok(Fallback::PubKeyHash(pkh))
546 if bytes.len() != 20 {
547 return Err(ParseError::InvalidScriptHashLength);
549 let mut sh = [0u8; 20];
550 sh.copy_from_slice(&bytes);
551 Ok(Fallback::ScriptHash(sh))
553 _ => Err(ParseError::Skip)
558 impl FromBase32 for RouteHint {
559 type Err = ParseError;
561 fn from_base32(field_data: &[u5]) -> Result<RouteHint, ParseError> {
562 let bytes = Vec::<u8>::from_base32(field_data)?;
564 if bytes.len() % 51 != 0 {
565 return Err(ParseError::UnexpectedEndOfTaggedFields);
568 let mut route_hops = Vec::<RouteHintHop>::new();
570 let mut bytes = bytes.as_slice();
571 while !bytes.is_empty() {
572 let hop_bytes = &bytes[0..51];
573 bytes = &bytes[51..];
575 let mut channel_id: [u8; 8] = Default::default();
576 channel_id.copy_from_slice(&hop_bytes[33..41]);
578 let hop = RouteHintHop {
579 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
580 short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
582 base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
583 proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
585 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
586 htlc_minimum_msat: None,
587 htlc_maximum_msat: None,
590 route_hops.push(hop);
593 Ok(RouteHint(route_hops))
597 /// Errors that indicate what is wrong with the invoice. They have some granularity for debug
598 /// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
599 #[allow(missing_docs)]
600 #[derive(PartialEq, Debug, Clone)]
601 pub enum ParseError {
602 Bech32Error(bech32::Error),
603 ParseAmountError(ParseIntError),
604 MalformedSignature(secp256k1::Error),
610 UnexpectedEndOfTaggedFields,
611 DescriptionDecodeError(str::Utf8Error),
613 IntegerOverflowError,
614 InvalidSegWitProgramLength,
615 InvalidPubKeyHashLength,
616 InvalidScriptHashLength,
618 InvalidSliceLength(String),
620 /// Not an error, but used internally to signal that a part of the invoice should be ignored
621 /// according to BOLT11
626 /// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
627 /// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
628 /// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
629 #[derive(PartialEq, Debug, Clone)]
630 pub enum ParseOrSemanticError {
631 /// The invoice couldn't be decoded
632 ParseError(ParseError),
634 /// The invoice could be decoded but violates the BOLT11 standard
635 SemanticError(::SemanticError),
638 impl Display for ParseError {
639 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
641 // TODO: find a way to combine the first three arms (e as error::Error?)
642 ParseError::Bech32Error(ref e) => {
643 write!(f, "Invalid bech32: {}", e)
645 ParseError::ParseAmountError(ref e) => {
646 write!(f, "Invalid amount in hrp ({})", e)
648 ParseError::MalformedSignature(ref e) => {
649 write!(f, "Invalid secp256k1 signature: {}", e)
651 ParseError::DescriptionDecodeError(ref e) => {
652 write!(f, "Description is not a valid utf-8 string: {}", e)
654 ParseError::InvalidSliceLength(ref function) => {
655 write!(f, "Slice in function {} had the wrong length", function)
657 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
658 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
659 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
660 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
661 ParseError::TooShortDataPart => {
662 f.write_str("data part too short (should be at least 111 bech32 chars long)")
664 ParseError::UnexpectedEndOfTaggedFields => {
665 f.write_str("tagged fields part ended unexpectedly")
667 ParseError::PaddingError => f.write_str("some data field had bad padding"),
668 ParseError::IntegerOverflowError => {
669 f.write_str("parsed integer doesn't fit into receiving type")
671 ParseError::InvalidSegWitProgramLength => {
672 f.write_str("fallback SegWit program is too long or too short")
674 ParseError::InvalidPubKeyHashLength => {
675 f.write_str("fallback public key hash has a length unequal 20 bytes")
677 ParseError::InvalidScriptHashLength => {
678 f.write_str("fallback script hash has a length unequal 32 bytes")
680 ParseError::InvalidRecoveryId => {
681 f.write_str("recovery id is out of range (should be in [0,3])")
683 ParseError::Skip => {
684 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
686 ParseError::TimestampOverflow => {
687 f.write_str("the invoice's timestamp could not be represented as SystemTime")
693 impl Display for ParseOrSemanticError {
694 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
696 ParseOrSemanticError::ParseError(err) => err.fmt(f),
697 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
702 impl error::Error for ParseError {}
704 impl error::Error for ParseOrSemanticError {}
706 macro_rules! from_error {
707 ($my_error:expr, $extern_error:ty) => {
708 impl From<$extern_error> for ParseError {
709 fn from(e: $extern_error) -> Self {
716 from_error!(ParseError::MalformedSignature, secp256k1::Error);
717 from_error!(ParseError::ParseAmountError, ParseIntError);
718 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
720 impl From<bech32::Error> for ParseError {
721 fn from(e: bech32::Error) -> Self {
723 bech32::Error::InvalidPadding => ParseError::PaddingError,
724 _ => ParseError::Bech32Error(e)
729 impl From<ParseError> for ParseOrSemanticError {
730 fn from(e: ParseError) -> Self {
731 ParseOrSemanticError::ParseError(e)
735 impl From<::SemanticError> for ParseOrSemanticError {
736 fn from(e: SemanticError) -> Self {
737 ParseOrSemanticError::SemanticError(e)
744 use secp256k1::PublicKey;
746 use bitcoin_hashes::hex::FromHex;
747 use bitcoin_hashes::sha256;
749 const CHARSET_REV: [i8; 128] = [
750 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
751 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
752 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
753 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
754 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
755 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
756 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
757 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
760 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
763 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
768 fn test_parse_currency_prefix() {
771 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
772 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
773 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
774 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
775 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
779 fn test_parse_int_from_bytes_be() {
780 use de::parse_int_be;
782 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
783 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
784 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
785 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
789 fn test_parse_sha256_hash() {
791 use bech32::FromBase32;
793 let input = from_bech32(
794 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
797 let hash = sha256::Hash::from_hex(
798 "0001020304050607080900010203040506070809000102030405060708090102"
800 let expected = Ok(Sha256(hash));
802 assert_eq!(Sha256::from_base32(&input), expected);
804 // make sure hashes of unknown length get skipped
805 let input_unexpected_length = from_bech32(
806 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
808 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
812 fn test_parse_description() {
814 use bech32::FromBase32;
816 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
817 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
818 assert_eq!(Description::from_base32(&input), expected);
822 fn test_parse_payee_pub_key() {
824 use bech32::FromBase32;
826 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
828 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
829 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
830 0x0f, 0x93, 0x4d, 0xd9, 0xad
832 let expected = Ok(PayeePubKey(
833 PublicKey::from_slice(&pk_bytes[..]).unwrap()
836 assert_eq!(PayeePubKey::from_base32(&input), expected);
839 let input_unexpected_length = from_bech32(
840 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
842 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
846 fn test_parse_expiry_time() {
848 use bech32::FromBase32;
850 let input = from_bech32("pu".as_bytes());
851 let expected = Ok(ExpiryTime::from_seconds(60).unwrap());
852 assert_eq!(ExpiryTime::from_base32(&input), expected);
854 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
855 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
859 fn test_parse_min_final_cltv_expiry() {
860 use ::MinFinalCltvExpiry;
861 use bech32::FromBase32;
863 let input = from_bech32("pr".as_bytes());
864 let expected = Ok(MinFinalCltvExpiry(35));
866 assert_eq!(MinFinalCltvExpiry::from_base32(&input), expected);
870 fn test_parse_fallback() {
872 use bech32::FromBase32;
876 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
877 Ok(Fallback::PubKeyHash([
878 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
879 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
883 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
884 Ok(Fallback::ScriptHash([
885 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
886 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
890 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
891 Ok(Fallback::SegWitProgram {
892 version: u5::try_from_u8(0).unwrap(),
893 program: Vec::from(&[
894 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
895 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
900 vec![u5::try_from_u8(21).unwrap(); 41],
901 Err(ParseError::Skip)
905 Err(ParseError::UnexpectedEndOfTaggedFields)
908 vec![u5::try_from_u8(1).unwrap(); 81],
909 Err(ParseError::InvalidSegWitProgramLength)
912 vec![u5::try_from_u8(17).unwrap(); 1],
913 Err(ParseError::InvalidPubKeyHashLength)
916 vec![u5::try_from_u8(18).unwrap(); 1],
917 Err(ParseError::InvalidScriptHashLength)
921 for (input, expected) in cases.into_iter() {
922 assert_eq!(Fallback::from_base32(&input), expected);
927 fn test_parse_route() {
928 use lightning::routing::network_graph::RoutingFees;
929 use lightning::routing::router::RouteHintHop;
931 use bech32::FromBase32;
932 use de::parse_int_be;
934 let input = from_bech32(
935 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
936 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
939 let mut expected = Vec::<RouteHintHop>::new();
940 expected.push(RouteHintHop {
941 src_node_id: PublicKey::from_slice(
943 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
944 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
945 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
948 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
951 proportional_millionths: 20,
953 cltv_expiry_delta: 3,
954 htlc_minimum_msat: None,
955 htlc_maximum_msat: None
957 expected.push(RouteHintHop {
958 src_node_id: 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: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
968 proportional_millionths: 30,
970 cltv_expiry_delta: 4,
971 htlc_minimum_msat: None,
972 htlc_maximum_msat: None
975 assert_eq!(RouteHint::from_base32(&input), Ok(RouteHint(expected)));
978 RouteHint::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
979 Err(ParseError::UnexpectedEndOfTaggedFields)
984 fn test_payment_secret_and_features_de_and_ser() {
985 use lightning::ln::features::InvoiceFeatures;
986 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
988 use {SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
989 Currency, Sha256, PositiveTimestamp};
991 // Feature bits 9, 15, and 99 are set.
992 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
993 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
994 let invoice = SignedRawInvoice {
995 raw_invoice: RawInvoice {
997 currency: Currency::Bitcoin,
998 raw_amount: Some(25),
999 si_prefix: Some(SiPrefix::Milli)
1002 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1003 tagged_fields: vec ! [
1004 PaymentHash(Sha256(sha256::Hash::from_hex(
1005 "0001020304050607080900010203040506070809000102030405060708090102"
1006 ).unwrap())).into(),
1007 Description(::Description::new("coffee beans".to_owned()).unwrap()).into(),
1008 PaymentSecret(::PaymentSecret([17; 32])).into(),
1009 Features(expected_features).into()]}
1011 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1012 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1013 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1014 signature: InvoiceSignature(RecoverableSignature::from_compact(
1015 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1016 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1017 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1018 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1019 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1020 RecoveryId::from_i32(1).unwrap()
1023 assert_eq!(invoice_str, invoice.to_string());
1025 invoice_str.parse(),
1031 fn test_raw_signed_invoice_deserialization() {
1033 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1034 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1038 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1039 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1040 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1041 Ok(SignedRawInvoice {
1042 raw_invoice: RawInvoice {
1044 currency: Currency::Bitcoin,
1049 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1050 tagged_fields: vec ! [
1051 PaymentHash(Sha256(sha256::Hash::from_hex(
1052 "0001020304050607080900010203040506070809000102030405060708090102"
1053 ).unwrap())).into(),
1056 "Please consider supporting this project".to_owned()
1063 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1064 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1065 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1067 signature: InvoiceSignature(RecoverableSignature::from_compact(
1069 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1070 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1071 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1072 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1073 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1074 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1076 RecoveryId::from_i32(0).unwrap()