1 #[cfg(feature = "std")]
4 use core::fmt::{Display, Formatter};
5 use core::num::ParseIntError;
7 use core::str::FromStr;
9 use bech32::{u5, FromBase32};
11 use bitcoin_hashes::Hash;
12 use bitcoin_hashes::sha256;
13 use crate::prelude::*;
14 use lightning::ln::PaymentSecret;
15 use lightning::routing::gossip::RoutingFees;
16 use lightning::routing::router::{RouteHint, RouteHintHop};
18 use num_traits::{CheckedAdd, CheckedMul};
20 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
21 use secp256k1::PublicKey;
23 use super::{Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
24 SemanticError, PrivateRoute, ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawInvoice,
25 constants, SignedRawInvoice, RawDataPart, 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> {
198 use crate::SiPrefix::*;
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);
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..])?;
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(_) => unreachable!(),
365 impl FromBase32 for InvoiceSignature {
366 type Err = ParseError;
367 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
368 if signature.len() != 104 {
369 return Err(ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into()));
371 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
372 let signature = &recoverable_signature_bytes[0..64];
373 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
375 Ok(InvoiceSignature(RecoverableSignature::from_compact(
382 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
383 where T: CheckedAdd + CheckedMul + From<u8> + Default,
386 digits.iter().fold(Some(Default::default()), |acc, b|
388 .and_then(|x| x.checked_mul(&base))
389 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
393 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
394 let mut parts = Vec::<RawTaggedField>::new();
397 while !data.is_empty() {
399 return Err(ParseError::UnexpectedEndOfTaggedFields);
402 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
403 // parse the length to find the end of the tagged field's data
404 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
405 let last_element = 3 + len;
407 if data.len() < last_element {
408 return Err(ParseError::UnexpectedEndOfTaggedFields);
411 // Get the tagged field's data slice
412 let field = &data[0..last_element];
414 // Set data slice to remaining data
415 data = &data[last_element..];
417 match TaggedField::from_base32(field) {
419 parts.push(RawTaggedField::KnownSemantics(field))
421 Err(ParseError::Skip)|Err(ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
422 parts.push(RawTaggedField::UnknownSemantics(field.into()))
424 Err(e) => {return Err(e)}
430 impl FromBase32 for TaggedField {
431 type Err = ParseError;
433 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
435 return Err(ParseError::UnexpectedEndOfTaggedFields);
439 let field_data = &field[3..];
442 constants::TAG_PAYMENT_HASH =>
443 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
444 constants::TAG_DESCRIPTION =>
445 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
446 constants::TAG_PAYEE_PUB_KEY =>
447 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
448 constants::TAG_DESCRIPTION_HASH =>
449 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
450 constants::TAG_EXPIRY_TIME =>
451 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
452 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA =>
453 Ok(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta::from_base32(field_data)?)),
454 constants::TAG_FALLBACK =>
455 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
456 constants::TAG_PRIVATE_ROUTE =>
457 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
458 constants::TAG_PAYMENT_SECRET =>
459 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
460 constants::TAG_FEATURES =>
461 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
463 // "A reader MUST skip over unknown fields"
464 Err(ParseError::Skip)
470 impl FromBase32 for Sha256 {
471 type Err = ParseError;
473 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
474 if field_data.len() != 52 {
475 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
476 Err(ParseError::Skip)
478 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
479 .expect("length was checked before (52 u5 -> 32 u8)")))
484 impl FromBase32 for Description {
485 type Err = ParseError;
487 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
488 let bytes = Vec::<u8>::from_base32(field_data)?;
489 let description = String::from(str::from_utf8(&bytes)?);
490 Ok(Description::new(description).expect(
491 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
496 impl FromBase32 for PayeePubKey {
497 type Err = ParseError;
499 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
500 if field_data.len() != 53 {
501 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
502 Err(ParseError::Skip)
504 let data_bytes = Vec::<u8>::from_base32(field_data)?;
505 let pub_key = PublicKey::from_slice(&data_bytes)?;
511 impl FromBase32 for ExpiryTime {
512 type Err = ParseError;
514 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
515 match parse_int_be::<u64, u5>(field_data, 32)
516 .map(ExpiryTime::from_seconds)
519 None => Err(ParseError::IntegerOverflowError),
524 impl FromBase32 for MinFinalCltvExpiryDelta {
525 type Err = ParseError;
527 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, ParseError> {
528 let expiry = parse_int_be::<u64, u5>(field_data, 32);
529 if let Some(expiry) = expiry {
530 Ok(MinFinalCltvExpiryDelta(expiry))
532 Err(ParseError::IntegerOverflowError)
537 impl FromBase32 for Fallback {
538 type Err = ParseError;
540 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
541 if field_data.is_empty() {
542 return Err(ParseError::UnexpectedEndOfTaggedFields);
545 let version = field_data[0];
546 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
548 match version.to_u8() {
550 if bytes.len() < 2 || bytes.len() > 40 {
551 return Err(ParseError::InvalidSegWitProgramLength);
554 Ok(Fallback::SegWitProgram {
560 if bytes.len() != 20 {
561 return Err(ParseError::InvalidPubKeyHashLength);
563 //TODO: refactor once const generics are available
564 let mut pkh = [0u8; 20];
565 pkh.copy_from_slice(&bytes);
566 Ok(Fallback::PubKeyHash(pkh))
569 if bytes.len() != 20 {
570 return Err(ParseError::InvalidScriptHashLength);
572 let mut sh = [0u8; 20];
573 sh.copy_from_slice(&bytes);
574 Ok(Fallback::ScriptHash(sh))
576 _ => Err(ParseError::Skip)
581 impl FromBase32 for PrivateRoute {
582 type Err = ParseError;
584 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, ParseError> {
585 let bytes = Vec::<u8>::from_base32(field_data)?;
587 if bytes.len() % 51 != 0 {
588 return Err(ParseError::UnexpectedEndOfTaggedFields);
591 let mut route_hops = Vec::<RouteHintHop>::new();
593 let mut bytes = bytes.as_slice();
594 while !bytes.is_empty() {
595 let hop_bytes = &bytes[0..51];
596 bytes = &bytes[51..];
598 let mut channel_id: [u8; 8] = Default::default();
599 channel_id.copy_from_slice(&hop_bytes[33..41]);
601 let hop = RouteHintHop {
602 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
603 short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
605 base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
606 proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
608 cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
609 htlc_minimum_msat: None,
610 htlc_maximum_msat: None,
613 route_hops.push(hop);
616 Ok(PrivateRoute(RouteHint(route_hops)))
620 impl Display for ParseError {
621 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
623 // TODO: find a way to combine the first three arms (e as error::Error?)
624 ParseError::Bech32Error(ref e) => {
625 write!(f, "Invalid bech32: {}", e)
627 ParseError::ParseAmountError(ref e) => {
628 write!(f, "Invalid amount in hrp ({})", e)
630 ParseError::MalformedSignature(ref e) => {
631 write!(f, "Invalid secp256k1 signature: {}", e)
633 ParseError::DescriptionDecodeError(ref e) => {
634 write!(f, "Description is not a valid utf-8 string: {}", e)
636 ParseError::InvalidSliceLength(ref function) => {
637 write!(f, "Slice in function {} had the wrong length", function)
639 ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
640 ParseError::UnknownCurrency => f.write_str("currency code unknown"),
641 ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
642 ParseError::MalformedHRP => f.write_str("malformed human readable part"),
643 ParseError::TooShortDataPart => {
644 f.write_str("data part too short (should be at least 111 bech32 chars long)")
646 ParseError::UnexpectedEndOfTaggedFields => {
647 f.write_str("tagged fields part ended unexpectedly")
649 ParseError::PaddingError => f.write_str("some data field had bad padding"),
650 ParseError::IntegerOverflowError => {
651 f.write_str("parsed integer doesn't fit into receiving type")
653 ParseError::InvalidSegWitProgramLength => {
654 f.write_str("fallback SegWit program is too long or too short")
656 ParseError::InvalidPubKeyHashLength => {
657 f.write_str("fallback public key hash has a length unequal 20 bytes")
659 ParseError::InvalidScriptHashLength => {
660 f.write_str("fallback script hash has a length unequal 32 bytes")
662 ParseError::InvalidRecoveryId => {
663 f.write_str("recovery id is out of range (should be in [0,3])")
665 ParseError::Skip => {
666 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
672 impl Display for ParseOrSemanticError {
673 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
675 ParseOrSemanticError::ParseError(err) => err.fmt(f),
676 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
681 #[cfg(feature = "std")]
682 impl error::Error for ParseError {}
684 #[cfg(feature = "std")]
685 impl error::Error for ParseOrSemanticError {}
687 macro_rules! from_error {
688 ($my_error:expr, $extern_error:ty) => {
689 impl From<$extern_error> for ParseError {
690 fn from(e: $extern_error) -> Self {
697 from_error!(ParseError::MalformedSignature, secp256k1::Error);
698 from_error!(ParseError::ParseAmountError, ParseIntError);
699 from_error!(ParseError::DescriptionDecodeError, str::Utf8Error);
701 impl From<bech32::Error> for ParseError {
702 fn from(e: bech32::Error) -> Self {
704 bech32::Error::InvalidPadding => ParseError::PaddingError,
705 _ => ParseError::Bech32Error(e)
710 impl From<ParseError> for ParseOrSemanticError {
711 fn from(e: ParseError) -> Self {
712 ParseOrSemanticError::ParseError(e)
716 impl From<crate::SemanticError> for ParseOrSemanticError {
717 fn from(e: SemanticError) -> Self {
718 ParseOrSemanticError::SemanticError(e)
724 use crate::de::ParseError;
725 use secp256k1::PublicKey;
727 use bitcoin_hashes::hex::FromHex;
728 use bitcoin_hashes::sha256;
730 const CHARSET_REV: [i8; 128] = [
731 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
732 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
733 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
734 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
735 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
736 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
737 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
738 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
741 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
744 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
749 fn test_parse_currency_prefix() {
752 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
753 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
754 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
755 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
756 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
757 assert_eq!("something_else".parse::<Currency>(), Err(ParseError::UnknownCurrency))
761 fn test_parse_int_from_bytes_be() {
762 use crate::de::parse_int_be;
764 assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
765 assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
766 assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
767 assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
771 fn test_parse_sha256_hash() {
773 use bech32::FromBase32;
775 let input = from_bech32(
776 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
779 let hash = sha256::Hash::from_hex(
780 "0001020304050607080900010203040506070809000102030405060708090102"
782 let expected = Ok(Sha256(hash));
784 assert_eq!(Sha256::from_base32(&input), expected);
786 // make sure hashes of unknown length get skipped
787 let input_unexpected_length = from_bech32(
788 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
790 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(ParseError::Skip));
794 fn test_parse_description() {
795 use crate::Description;
796 use bech32::FromBase32;
798 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
799 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
800 assert_eq!(Description::from_base32(&input), expected);
804 fn test_parse_payee_pub_key() {
805 use crate::PayeePubKey;
806 use bech32::FromBase32;
808 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
810 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
811 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
812 0x0f, 0x93, 0x4d, 0xd9, 0xad
814 let expected = Ok(PayeePubKey(
815 PublicKey::from_slice(&pk_bytes[..]).unwrap()
818 assert_eq!(PayeePubKey::from_base32(&input), expected);
821 let input_unexpected_length = from_bech32(
822 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
824 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(ParseError::Skip));
828 fn test_parse_expiry_time() {
829 use crate::ExpiryTime;
830 use bech32::FromBase32;
832 let input = from_bech32("pu".as_bytes());
833 let expected = Ok(ExpiryTime::from_seconds(60));
834 assert_eq!(ExpiryTime::from_base32(&input), expected);
836 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
837 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(ParseError::IntegerOverflowError));
841 fn test_parse_min_final_cltv_expiry_delta() {
842 use crate::MinFinalCltvExpiryDelta;
843 use bech32::FromBase32;
845 let input = from_bech32("pr".as_bytes());
846 let expected = Ok(MinFinalCltvExpiryDelta(35));
848 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
852 fn test_parse_fallback() {
854 use bech32::FromBase32;
858 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
859 Ok(Fallback::PubKeyHash([
860 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
861 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
865 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
866 Ok(Fallback::ScriptHash([
867 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
868 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
872 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
873 Ok(Fallback::SegWitProgram {
874 version: u5::try_from_u8(0).unwrap(),
875 program: Vec::from(&[
876 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
877 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
882 vec![u5::try_from_u8(21).unwrap(); 41],
883 Err(ParseError::Skip)
887 Err(ParseError::UnexpectedEndOfTaggedFields)
890 vec![u5::try_from_u8(1).unwrap(); 81],
891 Err(ParseError::InvalidSegWitProgramLength)
894 vec![u5::try_from_u8(17).unwrap(); 1],
895 Err(ParseError::InvalidPubKeyHashLength)
898 vec![u5::try_from_u8(18).unwrap(); 1],
899 Err(ParseError::InvalidScriptHashLength)
903 for (input, expected) in cases.into_iter() {
904 assert_eq!(Fallback::from_base32(&input), expected);
909 fn test_parse_route() {
910 use lightning::routing::gossip::RoutingFees;
911 use lightning::routing::router::{RouteHint, RouteHintHop};
912 use crate::PrivateRoute;
913 use bech32::FromBase32;
914 use crate::de::parse_int_be;
916 let input = from_bech32(
917 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
918 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
921 let mut expected = Vec::<RouteHintHop>::new();
922 expected.push(RouteHintHop {
923 src_node_id: PublicKey::from_slice(
925 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
926 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
927 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
930 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
933 proportional_millionths: 20,
935 cltv_expiry_delta: 3,
936 htlc_minimum_msat: None,
937 htlc_maximum_msat: None
939 expected.push(RouteHintHop {
940 src_node_id: PublicKey::from_slice(
942 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
943 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
944 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
947 short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
950 proportional_millionths: 30,
952 cltv_expiry_delta: 4,
953 htlc_minimum_msat: None,
954 htlc_maximum_msat: None
957 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
960 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
961 Err(ParseError::UnexpectedEndOfTaggedFields)
966 fn test_payment_secret_and_features_de_and_ser() {
967 use lightning::ln::features::InvoiceFeatures;
968 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
969 use crate::TaggedField::*;
970 use crate::{SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
971 Currency, Sha256, PositiveTimestamp};
973 // Feature bits 9, 15, and 99 are set.
974 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
975 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
976 let invoice = SignedRawInvoice {
977 raw_invoice: RawInvoice {
979 currency: Currency::Bitcoin,
980 raw_amount: Some(25),
981 si_prefix: Some(SiPrefix::Milli)
984 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
985 tagged_fields: vec ! [
986 PaymentHash(Sha256(sha256::Hash::from_hex(
987 "0001020304050607080900010203040506070809000102030405060708090102"
989 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
990 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
991 Features(expected_features).into()]}
993 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
994 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
995 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
996 signature: InvoiceSignature(RecoverableSignature::from_compact(
997 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
998 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
999 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1000 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1001 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1002 RecoveryId::from_i32(1).unwrap()
1005 assert_eq!(invoice_str, invoice.to_string());
1007 invoice_str.parse(),
1013 fn test_raw_signed_invoice_deserialization() {
1014 use crate::TaggedField::*;
1015 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
1016 use crate::{SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1020 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1021 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1022 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1023 Ok(SignedRawInvoice {
1024 raw_invoice: RawInvoice {
1026 currency: Currency::Bitcoin,
1031 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1032 tagged_fields: vec ! [
1033 PaymentHash(Sha256(sha256::Hash::from_hex(
1034 "0001020304050607080900010203040506070809000102030405060708090102"
1035 ).unwrap())).into(),
1037 crate::Description::new(
1038 "Please consider supporting this project".to_owned()
1045 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1046 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1047 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1049 signature: InvoiceSignature(RecoverableSignature::from_compact(
1051 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1052 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1053 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1054 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1055 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1056 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1058 RecoveryId::from_i32(0).unwrap()