1 #[cfg(feature = "std")]
3 use core::convert::TryFrom;
5 use core::fmt::{Display, Formatter};
6 use core::num::ParseIntError;
8 use core::str::FromStr;
10 use bech32::{u5, FromBase32};
12 use bitcoin::{PubkeyHash, ScriptHash};
13 use bitcoin::util::address::WitnessVersion;
14 use bitcoin_hashes::Hash;
15 use bitcoin_hashes::sha256;
16 use crate::prelude::*;
17 use lightning::ln::PaymentSecret;
18 use lightning::routing::gossip::RoutingFees;
19 use lightning::routing::router::{RouteHint, RouteHintHop};
21 use num_traits::{CheckedAdd, CheckedMul};
23 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
24 use secp256k1::PublicKey;
26 use super::{Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
27 SemanticError, PrivateRoute, ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawInvoice,
28 constants, SignedRawInvoice, RawDataPart, InvoiceFeatures};
30 use self::hrp_sm::parse_hrp;
32 /// State machine to parse the hrp
36 #[derive(PartialEq, Eq, Debug)]
47 fn next_state(&self, read_symbol: char) -> Result<States, super::ParseError> {
50 if read_symbol == 'l' {
53 Err(super::ParseError::MalformedHRP)
57 if read_symbol == 'n' {
60 Err(super::ParseError::MalformedHRP)
64 if !read_symbol.is_numeric() {
65 Ok(States::ParseCurrencyPrefix)
67 Ok(States::ParseAmountNumber)
70 States::ParseCurrencyPrefix => {
71 if !read_symbol.is_numeric() {
72 Ok(States::ParseCurrencyPrefix)
74 Ok(States::ParseAmountNumber)
77 States::ParseAmountNumber => {
78 if read_symbol.is_numeric() {
79 Ok(States::ParseAmountNumber)
80 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
81 Ok(States::ParseAmountSiPrefix)
83 Err(super::ParseError::UnknownSiPrefix)
86 States::ParseAmountSiPrefix => Err(super::ParseError::MalformedHRP),
90 fn is_final(&self) -> bool {
91 !(*self == States::ParseL || *self == States::ParseN)
99 currency_prefix: Option<Range<usize>>,
100 amount_number: Option<Range<usize>>,
101 amount_si_prefix: Option<Range<usize>>,
105 fn new() -> StateMachine {
107 state: States::Start,
109 currency_prefix: None,
111 amount_si_prefix: None,
115 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
116 let new_range = match *range {
117 None => Range {start: position, end: position + 1},
118 Some(ref r) => Range {start: r.start, end: r.end + 1},
120 *range = Some(new_range);
123 fn step(&mut self, c: char) -> Result<(), super::ParseError> {
124 let next_state = self.state.next_state(c)?;
126 States::ParseCurrencyPrefix => {
127 StateMachine::update_range(&mut self.currency_prefix, self.position)
129 States::ParseAmountNumber => {
130 StateMachine::update_range(&mut self.amount_number, self.position)
132 States::ParseAmountSiPrefix => {
133 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
139 self.state = next_state;
143 fn is_final(&self) -> bool {
144 self.state.is_final()
147 fn currency_prefix(&self) -> &Option<Range<usize>> {
148 &self.currency_prefix
151 fn amount_number(&self) -> &Option<Range<usize>> {
155 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
156 &self.amount_si_prefix
160 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::ParseError> {
161 let mut sm = StateMachine::new();
162 for c in input.chars() {
167 return Err(super::ParseError::MalformedHRP);
170 let currency = sm.currency_prefix().clone()
171 .map(|r| &input[r]).unwrap_or("");
172 let amount = sm.amount_number().clone()
173 .map(|r| &input[r]).unwrap_or("");
174 let si = sm.amount_si_prefix().clone()
175 .map(|r| &input[r]).unwrap_or("");
177 Ok((currency, amount, si))
182 impl FromStr for super::Currency {
183 type Err = ParseError;
185 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
186 match currency_prefix {
187 "bc" => Ok(Currency::Bitcoin),
188 "tb" => Ok(Currency::BitcoinTestnet),
189 "bcrt" => Ok(Currency::Regtest),
190 "sb" => Ok(Currency::Simnet),
191 "tbs" => Ok(Currency::Signet),
192 _ => Err(ParseError::UnknownCurrency)
197 impl FromStr for SiPrefix {
198 type Err = ParseError;
200 fn from_str(currency_prefix: &str) -> Result<Self, ParseError> {
201 use crate::SiPrefix::*;
202 match currency_prefix {
207 _ => Err(ParseError::UnknownSiPrefix)
213 /// use lightning_invoice::Invoice;
216 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
217 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
218 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
219 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
220 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
221 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
222 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
223 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
224 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
225 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
226 /// j5r6drg6k6zcqj0fcwg";
228 /// assert!(invoice.parse::<Invoice>().is_ok());
230 impl FromStr for Invoice {
231 type Err = ParseOrSemanticError;
233 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
234 let signed = s.parse::<SignedRawInvoice>()?;
235 Ok(Invoice::from_signed(signed)?)
240 /// use lightning_invoice::*;
242 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
243 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
244 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
245 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
246 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
247 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
248 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
249 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
250 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
251 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
252 /// j5r6drg6k6zcqj0fcwg";
254 /// let parsed_1 = invoice.parse::<Invoice>();
256 /// let parsed_2 = match invoice.parse::<SignedRawInvoice>() {
257 /// Ok(signed) => match Invoice::from_signed(signed) {
258 /// Ok(invoice) => Ok(invoice),
259 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
261 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
264 /// assert!(parsed_1.is_ok());
265 /// assert_eq!(parsed_1, parsed_2);
267 impl FromStr for SignedRawInvoice {
268 type Err = ParseError;
270 fn from_str(s: &str) -> Result<Self, Self::Err> {
271 let (hrp, data, var) = bech32::decode(s)?;
273 if var == bech32::Variant::Bech32m {
274 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
275 // we didn't support Bech32m (which lightning does not use).
276 return Err(ParseError::Bech32Error(bech32::Error::InvalidChecksum));
279 if data.len() < 104 {
280 return Err(ParseError::TooShortDataPart);
283 let raw_hrp: RawHrp = hrp.parse()?;
284 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
286 Ok(SignedRawInvoice {
287 raw_invoice: RawInvoice {
291 hash: RawInvoice::hash_from_parts(
293 &data[..data.len()-104]
295 signature: InvoiceSignature::from_base32(&data[data.len()-104..])?,
300 impl FromStr for RawHrp {
301 type Err = ParseError;
303 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
304 let parts = parse_hrp(hrp)?;
306 let currency = parts.0.parse::<Currency>()?;
308 let amount = if !parts.1.is_empty() {
309 Some(parts.1.parse::<u64>()?)
314 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
317 let si: SiPrefix = parts.2.parse()?;
318 if let Some(amt) = amount {
319 if amt.checked_mul(si.multiplier()).is_none() {
320 return Err(ParseError::IntegerOverflowError);
334 impl FromBase32 for RawDataPart {
335 type Err = ParseError;
337 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
338 if data.len() < 7 { // timestamp length
339 return Err(ParseError::TooShortDataPart);
342 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
343 let tagged = parse_tagged_parts(&data[7..])?;
347 tagged_fields: tagged,
352 impl FromBase32 for PositiveTimestamp {
353 type Err = ParseError;
355 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
357 return Err(ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
359 let timestamp: u64 = parse_int_be(b32, 32)
360 .expect("7*5bit < 64bit, no overflow possible");
361 match PositiveTimestamp::from_unix_timestamp(timestamp) {
363 Err(_) => unreachable!(),
368 impl FromBase32 for InvoiceSignature {
369 type Err = ParseError;
370 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
371 if signature.len() != 104 {
372 return Err(ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into()));
374 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
375 let signature = &recoverable_signature_bytes[0..64];
376 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
378 Ok(InvoiceSignature(RecoverableSignature::from_compact(
385 pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
386 where T: CheckedAdd + CheckedMul + From<u8> + Default,
389 digits.iter().fold(Some(Default::default()), |acc, b|
391 .and_then(|x| x.checked_mul(&base))
392 .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
396 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, ParseError> {
397 let mut parts = Vec::<RawTaggedField>::new();
400 while !data.is_empty() {
402 return Err(ParseError::UnexpectedEndOfTaggedFields);
405 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
406 // parse the length to find the end of the tagged field's data
407 let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
408 let last_element = 3 + len;
410 if data.len() < last_element {
411 return Err(ParseError::UnexpectedEndOfTaggedFields);
414 // Get the tagged field's data slice
415 let field = &data[0..last_element];
417 // Set data slice to remaining data
418 data = &data[last_element..];
420 match TaggedField::from_base32(field) {
422 parts.push(RawTaggedField::KnownSemantics(field))
424 Err(ParseError::Skip)|Err(ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
425 parts.push(RawTaggedField::UnknownSemantics(field.into()))
427 Err(e) => {return Err(e)}
433 impl FromBase32 for TaggedField {
434 type Err = ParseError;
436 fn from_base32(field: &[u5]) -> Result<TaggedField, ParseError> {
438 return Err(ParseError::UnexpectedEndOfTaggedFields);
442 let field_data = &field[3..];
445 constants::TAG_PAYMENT_HASH =>
446 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
447 constants::TAG_DESCRIPTION =>
448 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
449 constants::TAG_PAYEE_PUB_KEY =>
450 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
451 constants::TAG_DESCRIPTION_HASH =>
452 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
453 constants::TAG_EXPIRY_TIME =>
454 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
455 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA =>
456 Ok(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta::from_base32(field_data)?)),
457 constants::TAG_FALLBACK =>
458 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
459 constants::TAG_PRIVATE_ROUTE =>
460 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
461 constants::TAG_PAYMENT_SECRET =>
462 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
463 constants::TAG_FEATURES =>
464 Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)),
466 // "A reader MUST skip over unknown fields"
467 Err(ParseError::Skip)
473 impl FromBase32 for Sha256 {
474 type Err = ParseError;
476 fn from_base32(field_data: &[u5]) -> Result<Sha256, ParseError> {
477 if field_data.len() != 52 {
478 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
479 Err(ParseError::Skip)
481 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
482 .expect("length was checked before (52 u5 -> 32 u8)")))
487 impl FromBase32 for Description {
488 type Err = ParseError;
490 fn from_base32(field_data: &[u5]) -> Result<Description, ParseError> {
491 let bytes = Vec::<u8>::from_base32(field_data)?;
492 let description = String::from(str::from_utf8(&bytes)?);
493 Ok(Description::new(description).expect(
494 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
499 impl FromBase32 for PayeePubKey {
500 type Err = ParseError;
502 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, ParseError> {
503 if field_data.len() != 53 {
504 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
505 Err(ParseError::Skip)
507 let data_bytes = Vec::<u8>::from_base32(field_data)?;
508 let pub_key = PublicKey::from_slice(&data_bytes)?;
514 impl FromBase32 for ExpiryTime {
515 type Err = ParseError;
517 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
518 match parse_int_be::<u64, u5>(field_data, 32)
519 .map(ExpiryTime::from_seconds)
522 None => Err(ParseError::IntegerOverflowError),
527 impl FromBase32 for MinFinalCltvExpiryDelta {
528 type Err = ParseError;
530 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, ParseError> {
531 let expiry = parse_int_be::<u64, u5>(field_data, 32);
532 if let Some(expiry) = expiry {
533 Ok(MinFinalCltvExpiryDelta(expiry))
535 Err(ParseError::IntegerOverflowError)
540 impl FromBase32 for Fallback {
541 type Err = ParseError;
543 fn from_base32(field_data: &[u5]) -> Result<Fallback, ParseError> {
544 if field_data.is_empty() {
545 return Err(ParseError::UnexpectedEndOfTaggedFields);
548 let version = field_data[0];
549 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
551 match version.to_u8() {
553 if bytes.len() < 2 || bytes.len() > 40 {
554 return Err(ParseError::InvalidSegWitProgramLength);
556 let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
557 Ok(Fallback::SegWitProgram {
563 let pkh = match PubkeyHash::from_slice(&bytes) {
565 Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(ParseError::InvalidPubKeyHashLength),
567 Ok(Fallback::PubKeyHash(pkh))
570 let sh = match ScriptHash::from_slice(&bytes) {
572 Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(ParseError::InvalidScriptHashLength),
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;
855 use bitcoin::{PubkeyHash, ScriptHash};
856 use bitcoin::util::address::WitnessVersion;
857 use bitcoin_hashes::Hash;
861 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
862 Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
863 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
864 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
868 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
869 Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
870 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
871 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
875 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
876 Ok(Fallback::SegWitProgram {
877 version: WitnessVersion::V0,
878 program: Vec::from(&[
879 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
880 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
885 vec![u5::try_from_u8(21).unwrap(); 41],
886 Err(ParseError::Skip)
890 Err(ParseError::UnexpectedEndOfTaggedFields)
893 vec![u5::try_from_u8(1).unwrap(); 81],
894 Err(ParseError::InvalidSegWitProgramLength)
897 vec![u5::try_from_u8(17).unwrap(); 1],
898 Err(ParseError::InvalidPubKeyHashLength)
901 vec![u5::try_from_u8(18).unwrap(); 1],
902 Err(ParseError::InvalidScriptHashLength)
906 for (input, expected) in cases.into_iter() {
907 assert_eq!(Fallback::from_base32(&input), expected);
912 fn test_parse_route() {
913 use lightning::routing::gossip::RoutingFees;
914 use lightning::routing::router::{RouteHint, RouteHintHop};
915 use crate::PrivateRoute;
916 use bech32::FromBase32;
917 use crate::de::parse_int_be;
919 let input = from_bech32(
920 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
921 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
924 let mut expected = Vec::<RouteHintHop>::new();
925 expected.push(RouteHintHop {
926 src_node_id: PublicKey::from_slice(
928 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
929 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
930 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
933 short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
936 proportional_millionths: 20,
938 cltv_expiry_delta: 3,
939 htlc_minimum_msat: None,
940 htlc_maximum_msat: None
942 expected.push(RouteHintHop {
943 src_node_id: PublicKey::from_slice(
945 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
946 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
947 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
950 short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
953 proportional_millionths: 30,
955 cltv_expiry_delta: 4,
956 htlc_minimum_msat: None,
957 htlc_maximum_msat: None
960 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
963 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
964 Err(ParseError::UnexpectedEndOfTaggedFields)
969 fn test_payment_secret_and_features_de_and_ser() {
970 use lightning::ln::features::InvoiceFeatures;
971 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
972 use crate::TaggedField::*;
973 use crate::{SiPrefix, SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart,
974 Currency, Sha256, PositiveTimestamp};
976 // Feature bits 9, 15, and 99 are set.
977 let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
978 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
979 let invoice = SignedRawInvoice {
980 raw_invoice: RawInvoice {
982 currency: Currency::Bitcoin,
983 raw_amount: Some(25),
984 si_prefix: Some(SiPrefix::Milli)
987 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
988 tagged_fields: vec ! [
989 PaymentHash(Sha256(sha256::Hash::from_hex(
990 "0001020304050607080900010203040506070809000102030405060708090102"
992 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
993 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
994 Features(expected_features).into()]}
996 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
997 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
998 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
999 signature: InvoiceSignature(RecoverableSignature::from_compact(
1000 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1001 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1002 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1003 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1004 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1005 RecoveryId::from_i32(1).unwrap()
1008 assert_eq!(invoice_str, invoice.to_string());
1010 invoice_str.parse(),
1016 fn test_raw_signed_invoice_deserialization() {
1017 use crate::TaggedField::*;
1018 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
1019 use crate::{SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1023 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1024 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1025 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1026 Ok(SignedRawInvoice {
1027 raw_invoice: RawInvoice {
1029 currency: Currency::Bitcoin,
1034 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1035 tagged_fields: vec ! [
1036 PaymentHash(Sha256(sha256::Hash::from_hex(
1037 "0001020304050607080900010203040506070809000102030405060708090102"
1038 ).unwrap())).into(),
1040 crate::Description::new(
1041 "Please consider supporting this project".to_owned()
1048 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1049 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1050 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1052 signature: InvoiceSignature(RecoverableSignature::from_compact(
1054 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1055 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1056 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1057 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1058 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1059 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1061 RecoveryId::from_i32(0).unwrap()