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::UnknownSiPrefix)
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;
213 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
214 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
215 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
216 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
217 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
218 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
219 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
220 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
221 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
222 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
223 /// j5r6drg6k6zcqj0fcwg";
225 /// assert!(invoice.parse::<Invoice>().is_ok());
227 impl FromStr for Invoice {
228 type Err = ParseOrSemanticError;
230 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
231 let signed = s.parse::<SignedRawInvoice>()?;
232 Ok(Invoice::from_signed(signed)?)
237 /// use lightning_invoice::*;
239 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
240 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
241 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
242 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
243 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
244 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
245 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
246 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
247 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
248 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
249 /// j5r6drg6k6zcqj0fcwg";
251 /// let parsed_1 = invoice.parse::<Invoice>();
253 /// let parsed_2 = match invoice.parse::<SignedRawInvoice>() {
254 /// Ok(signed) => match Invoice::from_signed(signed) {
255 /// Ok(invoice) => Ok(invoice),
256 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
258 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
261 /// assert!(parsed_1.is_ok());
262 /// assert_eq!(parsed_1, parsed_2);
264 impl FromStr for SignedRawInvoice {
265 type Err = ParseError;
267 fn from_str(s: &str) -> Result<Self, Self::Err> {
268 let (hrp, data, var) = bech32::decode(s)?;
270 if var == bech32::Variant::Bech32m {
271 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
272 // we didn't support Bech32m (which lightning does not use).
273 return Err(ParseError::Bech32Error(bech32::Error::InvalidChecksum));
276 if data.len() < 104 {
277 return Err(ParseError::TooShortDataPart);
280 let raw_hrp: RawHrp = hrp.parse()?;
281 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
283 Ok(SignedRawInvoice {
284 raw_invoice: RawInvoice {
288 hash: RawInvoice::hash_from_parts(
290 &data[..data.len()-104]
292 signature: InvoiceSignature::from_base32(&data[data.len()-104..])?,
297 impl FromStr for RawHrp {
298 type Err = ParseError;
300 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
301 let parts = parse_hrp(hrp)?;
303 let currency = parts.0.parse::<Currency>()?;
305 let amount = if !parts.1.is_empty() {
306 Some(parts.1.parse::<u64>()?)
311 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
314 let si: SiPrefix = parts.2.parse()?;
315 if let Some(amt) = amount {
316 if amt.checked_mul(si.multiplier()).is_none() {
317 return Err(ParseError::IntegerOverflowError);
326 si_prefix: si_prefix,
331 impl FromBase32 for RawDataPart {
332 type Err = ParseError;
334 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
335 if data.len() < 7 { // timestamp length
336 return Err(ParseError::TooShortDataPart);
339 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
340 let tagged = parse_tagged_parts(&data[7..])?;
343 timestamp: timestamp,
344 tagged_fields: tagged,
349 impl FromBase32 for PositiveTimestamp {
350 type Err = ParseError;
352 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
354 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
356 let timestamp: u64 = parse_int_be(b32, 32)
357 .expect("7*5bit < 64bit, no overflow possible");
358 match PositiveTimestamp::from_unix_timestamp(timestamp) {
360 Err(CreationError::TimestampOutOfBounds) => Err(ParseError::TimestampOverflow),
361 Err(_) => unreachable!(),
366 impl FromBase32 for InvoiceSignature {
367 type Err = ParseError;
368 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
369 if signature.len() != 104 {
370 return Err(ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into()));
372 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
373 let signature = &recoverable_signature_bytes[0..64];
374 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
376 Ok(InvoiceSignature(RecoverableSignature::from_compact(
383 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
384 where T: CheckedAdd + CheckedMul + From<u8> + Default,
387 digits.iter().fold(Some(Default::default()), |acc, b|
389 .and_then(|x| x.checked_mul(&base))
390 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
394 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
395 let mut parts = Vec::<RawTaggedField>::new();
398 while !data.is_empty() {
400 return Err(ParseError::UnexpectedEndOfTaggedFields);
403 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
404 // parse the length to find the end of the tagged field's data
405 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
406 let last_element = 3 + len;
408 if data.len() < last_element {
409 return Err(ParseError::UnexpectedEndOfTaggedFields);
412 // Get the tagged field's data slice
413 let field = &data[0..last_element];
415 // Set data slice to remaining data
416 data = &data[last_element..];
418 match TaggedField::from_base32(field) {
420 parts.push(RawTaggedField::KnownSemantics(field))
422 Err(ParseError::Skip) => {
423 parts.push(RawTaggedField::UnknownSemantics(field.into()))
425 Err(e) => {return Err(e)}
431 impl FromBase32 for TaggedField {
432 type Err = ParseError;
434 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
436 return Err(ParseError::UnexpectedEndOfTaggedFields);
440 let field_data = &field[3..];
443 constants::TAG_PAYMENT_HASH =>
444 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
445 constants::TAG_DESCRIPTION =>
446 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
447 constants::TAG_PAYEE_PUB_KEY =>
448 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
449 constants::TAG_DESCRIPTION_HASH =>
450 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
451 constants::TAG_EXPIRY_TIME =>
452 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
453 constants::TAG_MIN_FINAL_CLTV_EXPIRY =>
454 Ok(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry::from_base32(field_data)?)),
455 constants::TAG_FALLBACK =>
456 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
457 constants::TAG_PRIVATE_ROUTE =>
458 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
459 constants::TAG_PAYMENT_SECRET =>
460 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
461 constants::TAG_FEATURES =>
462 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
464 // "A reader MUST skip over unknown fields"
465 Err(ParseError::Skip)
471 impl FromBase32 for Sha256 {
472 type Err = ParseError;
474 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
475 if field_data.len() != 52 {
476 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
477 Err(ParseError::Skip)
479 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
480 .expect("length was checked before (52 u5 -> 32 u8)")))
485 impl FromBase32 for Description {
486 type Err = ParseError;
488 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
489 let bytes = Vec::<u8>::from_base32(field_data)?;
490 let description = String::from(str::from_utf8(&bytes)?);
491 Ok(Description::new(description).expect(
492 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
497 impl FromBase32 for PayeePubKey {
498 type Err = ParseError;
500 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
501 if field_data.len() != 53 {
502 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
503 Err(ParseError::Skip)
505 let data_bytes = Vec::<u8>::from_base32(field_data)?;
506 let pub_key = PublicKey::from_slice(&data_bytes)?;
512 impl FromBase32 for ExpiryTime {
513 type Err = ParseError;
515 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
516 match parse_int_be::<u64, u5>(field_data, 32)
517 .and_then(|t| ExpiryTime::from_seconds(t).ok()) // ok, since the only error is out of bounds
520 None => Err(ParseError::IntegerOverflowError),
525 impl FromBase32 for MinFinalCltvExpiry {
526 type Err = ParseError;
528 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiry, ParseError> {
529 let expiry = parse_int_be::<u64, u5>(field_data, 32);
530 if let Some(expiry) = expiry {
531 Ok(MinFinalCltvExpiry(expiry))
533 Err(ParseError::IntegerOverflowError)
538 impl FromBase32 for Fallback {
539 type Err = ParseError;
541 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
542 if field_data.len() < 1 {
543 return Err(ParseError::UnexpectedEndOfTaggedFields);
546 let version = field_data[0];
547 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
549 match version.to_u8() {
551 if bytes.len() < 2 || bytes.len() > 40 {
552 return Err(ParseError::InvalidSegWitProgramLength);
555 Ok(Fallback::SegWitProgram {
561 if bytes.len() != 20 {
562 return Err(ParseError::InvalidPubKeyHashLength);
564 //TODO: refactor once const generics are available
565 let mut pkh = [0u8; 20];
566 pkh.copy_from_slice(&bytes);
567 Ok(Fallback::PubKeyHash(pkh))
570 if bytes.len() != 20 {
571 return Err(ParseError::InvalidScriptHashLength);
573 let mut sh = [0u8; 20];
574 sh.copy_from_slice(&bytes);
575 Ok(Fallback::ScriptHash(sh))
577 _ => Err(ParseError::Skip)
582 impl FromBase32 for PrivateRoute {
583 type Err = ParseError;
585 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, ParseError> {
586 let bytes = Vec::<u8>::from_base32(field_data)?;
588 if bytes.len() % 51 != 0 {
589 return Err(ParseError::UnexpectedEndOfTaggedFields);
592 let mut route_hops = Vec::<RouteHintHop>::new();
594 let mut bytes = bytes.as_slice();
595 while !bytes.is_empty() {
596 let hop_bytes = &bytes[0..51];
597 bytes = &bytes[51..];
599 let mut channel_id: [u8; 8] = Default::default();
600 channel_id.copy_from_slice(&hop_bytes[33..41]);
602 let hop = RouteHintHop {
603 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
604 short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
606 base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
607 proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
609 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
610 htlc_minimum_msat: None,
611 htlc_maximum_msat: None,
614 route_hops.push(hop);
617 Ok(PrivateRoute(RouteHint(route_hops)))
621 /// Errors that indicate what is wrong with the invoice. They have some granularity for debug
622 /// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
623 #[allow(missing_docs)]
624 #[derive(PartialEq, Debug, Clone)]
625 pub enum ParseError {
626 Bech32Error(bech32::Error),
627 ParseAmountError(ParseIntError),
628 MalformedSignature(secp256k1::Error),
634 UnexpectedEndOfTaggedFields,
635 DescriptionDecodeError(str::Utf8Error),
637 IntegerOverflowError,
638 InvalidSegWitProgramLength,
639 InvalidPubKeyHashLength,
640 InvalidScriptHashLength,
642 InvalidSliceLength(String),
644 /// Not an error, but used internally to signal that a part of the invoice should be ignored
645 /// according to BOLT11
650 /// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
651 /// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
652 /// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
653 #[derive(PartialEq, Debug, Clone)]
654 pub enum ParseOrSemanticError {
655 /// The invoice couldn't be decoded
656 ParseError(ParseError),
658 /// The invoice could be decoded but violates the BOLT11 standard
659 SemanticError(::SemanticError),
662 impl Display for ParseError {
663 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
665 // TODO: find a way to combine the first three arms (e as error::Error?)
666 ParseError::Bech32Error(ref e) => {
667 write!(f, "Invalid bech32: {}", e)
669 ParseError::ParseAmountError(ref e) => {
670 write!(f, "Invalid amount in hrp ({})", e)
672 ParseError::MalformedSignature(ref e) => {
673 write!(f, "Invalid secp256k1 signature: {}", e)
675 ParseError::DescriptionDecodeError(ref e) => {
676 write!(f, "Description is not a valid utf-8 string: {}", e)
678 ParseError::InvalidSliceLength(ref function) => {
679 write!(f, "Slice in function {} had the wrong length", function)
681 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
682 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
683 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
684 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
685 ParseError::TooShortDataPart => {
686 f.write_str("data part too short (should be at least 111 bech32 chars long)")
688 ParseError::UnexpectedEndOfTaggedFields => {
689 f.write_str("tagged fields part ended unexpectedly")
691 ParseError::PaddingError => f.write_str("some data field had bad padding"),
692 ParseError::IntegerOverflowError => {
693 f.write_str("parsed integer doesn't fit into receiving type")
695 ParseError::InvalidSegWitProgramLength => {
696 f.write_str("fallback SegWit program is too long or too short")
698 ParseError::InvalidPubKeyHashLength => {
699 f.write_str("fallback public key hash has a length unequal 20 bytes")
701 ParseError::InvalidScriptHashLength => {
702 f.write_str("fallback script hash has a length unequal 32 bytes")
704 ParseError::InvalidRecoveryId => {
705 f.write_str("recovery id is out of range (should be in [0,3])")
707 ParseError::Skip => {
708 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
710 ParseError::TimestampOverflow => {
711 f.write_str("the invoice's timestamp could not be represented as SystemTime")
717 impl Display for ParseOrSemanticError {
718 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
720 ParseOrSemanticError::ParseError(err) => err.fmt(f),
721 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
726 impl error::Error for ParseError {}
728 impl error::Error for ParseOrSemanticError {}
730 macro_rules! from_error {
731 ($my_error:expr, $extern_error:ty) => {
732 impl From<$extern_error> for ParseError {
733 fn from(e: $extern_error) -> Self {
740 from_error!(ParseError::MalformedSignature, secp256k1::Error);
741 from_error!(ParseError::ParseAmountError, ParseIntError);
742 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
744 impl From<bech32::Error> for ParseError {
745 fn from(e: bech32::Error) -> Self {
747 bech32::Error::InvalidPadding => ParseError::PaddingError,
748 _ => ParseError::Bech32Error(e)
753 impl From<ParseError> for ParseOrSemanticError {
754 fn from(e: ParseError) -> Self {
755 ParseOrSemanticError::ParseError(e)
759 impl From<::SemanticError> for ParseOrSemanticError {
760 fn from(e: SemanticError) -> Self {
761 ParseOrSemanticError::SemanticError(e)
768 use secp256k1::PublicKey;
770 use bitcoin_hashes::hex::FromHex;
771 use bitcoin_hashes::sha256;
773 const CHARSET_REV: [i8; 128] = [
774 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
775 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
776 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
777 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
778 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
779 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
780 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
781 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
784 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
787 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
792 fn test_parse_currency_prefix() {
795 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
796 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
797 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
798 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
799 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
800 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
804 fn test_parse_int_from_bytes_be() {
805 use de::parse_int_be;
807 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
808 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
809 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
810 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
814 fn test_parse_sha256_hash() {
816 use bech32::FromBase32;
818 let input = from_bech32(
819 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
822 let hash = sha256::Hash::from_hex(
823 "0001020304050607080900010203040506070809000102030405060708090102"
825 let expected = Ok(Sha256(hash));
827 assert_eq!(Sha256::from_base32(&input), expected);
829 // make sure hashes of unknown length get skipped
830 let input_unexpected_length = from_bech32(
831 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
833 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
837 fn test_parse_description() {
839 use bech32::FromBase32;
841 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
842 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
843 assert_eq!(Description::from_base32(&input), expected);
847 fn test_parse_payee_pub_key() {
849 use bech32::FromBase32;
851 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
853 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
854 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
855 0x0f, 0x93, 0x4d, 0xd9, 0xad
857 let expected = Ok(PayeePubKey(
858 PublicKey::from_slice(&pk_bytes[..]).unwrap()
861 assert_eq!(PayeePubKey::from_base32(&input), expected);
864 let input_unexpected_length = from_bech32(
865 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
867 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
871 fn test_parse_expiry_time() {
873 use bech32::FromBase32;
875 let input = from_bech32("pu".as_bytes());
876 let expected = Ok(ExpiryTime::from_seconds(60).unwrap());
877 assert_eq!(ExpiryTime::from_base32(&input), expected);
879 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
880 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
884 fn test_parse_min_final_cltv_expiry() {
885 use ::MinFinalCltvExpiry;
886 use bech32::FromBase32;
888 let input = from_bech32("pr".as_bytes());
889 let expected = Ok(MinFinalCltvExpiry(35));
891 assert_eq!(MinFinalCltvExpiry::from_base32(&input), expected);
895 fn test_parse_fallback() {
897 use bech32::FromBase32;
901 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
902 Ok(Fallback::PubKeyHash([
903 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
904 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
908 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
909 Ok(Fallback::ScriptHash([
910 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
911 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
915 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
916 Ok(Fallback::SegWitProgram {
917 version: u5::try_from_u8(0).unwrap(),
918 program: Vec::from(&[
919 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
920 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
925 vec![u5::try_from_u8(21).unwrap(); 41],
926 Err(ParseError::Skip)
930 Err(ParseError::UnexpectedEndOfTaggedFields)
933 vec![u5::try_from_u8(1).unwrap(); 81],
934 Err(ParseError::InvalidSegWitProgramLength)
937 vec![u5::try_from_u8(17).unwrap(); 1],
938 Err(ParseError::InvalidPubKeyHashLength)
941 vec![u5::try_from_u8(18).unwrap(); 1],
942 Err(ParseError::InvalidScriptHashLength)
946 for (input, expected) in cases.into_iter() {
947 assert_eq!(Fallback::from_base32(&input), expected);
952 fn test_parse_route() {
953 use lightning::routing::network_graph::RoutingFees;
954 use lightning::routing::router::{RouteHint, RouteHintHop};
956 use bech32::FromBase32;
957 use de::parse_int_be;
959 let input = from_bech32(
960 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
961 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
964 let mut expected = Vec::<RouteHintHop>::new();
965 expected.push(RouteHintHop {
966 src_node_id: PublicKey::from_slice(
968 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
969 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
970 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
973 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
976 proportional_millionths: 20,
978 cltv_expiry_delta: 3,
979 htlc_minimum_msat: None,
980 htlc_maximum_msat: None
982 expected.push(RouteHintHop {
983 src_node_id: PublicKey::from_slice(
985 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
986 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
987 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
990 short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
993 proportional_millionths: 30,
995 cltv_expiry_delta: 4,
996 htlc_minimum_msat: None,
997 htlc_maximum_msat: None
1000 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
1003 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
1004 Err(ParseError::UnexpectedEndOfTaggedFields)
1009 fn test_payment_secret_and_features_de_and_ser() {
1010 use lightning::ln::features::InvoiceFeatures;
1011 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1013 use {SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
1014 Currency, Sha256, PositiveTimestamp};
1016 // Feature bits 9, 15, and 99 are set.
1017 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
1018 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
1019 let invoice = SignedRawInvoice {
1020 raw_invoice: RawInvoice {
1022 currency: Currency::Bitcoin,
1023 raw_amount: Some(25),
1024 si_prefix: Some(SiPrefix::Milli)
1027 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1028 tagged_fields: vec ! [
1029 PaymentHash(Sha256(sha256::Hash::from_hex(
1030 "0001020304050607080900010203040506070809000102030405060708090102"
1031 ).unwrap())).into(),
1032 Description(::Description::new("coffee beans".to_owned()).unwrap()).into(),
1033 PaymentSecret(::PaymentSecret([17; 32])).into(),
1034 Features(expected_features).into()]}
1036 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1037 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1038 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1039 signature: InvoiceSignature(RecoverableSignature::from_compact(
1040 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1041 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1042 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1043 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1044 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1045 RecoveryId::from_i32(1).unwrap()
1048 assert_eq!(invoice_str, invoice.to_string());
1050 invoice_str.parse(),
1056 fn test_raw_signed_invoice_deserialization() {
1058 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1059 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1063 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1064 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1065 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1066 Ok(SignedRawInvoice {
1067 raw_invoice: RawInvoice {
1069 currency: Currency::Bitcoin,
1074 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1075 tagged_fields: vec ! [
1076 PaymentHash(Sha256(sha256::Hash::from_hex(
1077 "0001020304050607080900010203040506070809000102030405060708090102"
1078 ).unwrap())).into(),
1081 "Please consider supporting this project".to_owned()
1088 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1089 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1090 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1092 signature: InvoiceSignature(RecoverableSignature::from_compact(
1094 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1095 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1096 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1097 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1098 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1099 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1101 RecoveryId::from_i32(0).unwrap()