1 #[cfg(feature = "std")]
3 #[cfg(not(feature = "std"))]
4 use core::convert::TryFrom;
6 use core::fmt::{Display, Formatter};
7 use core::num::ParseIntError;
9 use core::str::FromStr;
11 use bech32::{u5, FromBase32};
13 use bitcoin::{PubkeyHash, ScriptHash};
14 use bitcoin::address::WitnessVersion;
15 use bitcoin::hashes::Hash;
16 use bitcoin::hashes::sha256;
17 use crate::prelude::*;
18 use lightning::ln::types::PaymentSecret;
19 use lightning::routing::gossip::RoutingFees;
20 use lightning::routing::router::{RouteHint, RouteHintHop};
22 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
23 use secp256k1::PublicKey;
25 use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
26 Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
27 constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures};
29 use self::hrp_sm::parse_hrp;
31 /// State machine to parse the hrp
35 #[derive(PartialEq, Eq, Debug)]
46 fn next_state(&self, read_byte: u8) -> Result<States, super::Bolt11ParseError> {
47 let read_symbol = match char::from_u32(read_byte.into()) {
48 Some(symb) if symb.is_ascii() => symb,
49 _ => return Err(super::Bolt11ParseError::MalformedHRP),
53 if read_symbol == 'l' {
56 Err(super::Bolt11ParseError::MalformedHRP)
60 if read_symbol == 'n' {
63 Err(super::Bolt11ParseError::MalformedHRP)
67 if !read_symbol.is_numeric() {
68 Ok(States::ParseCurrencyPrefix)
70 Ok(States::ParseAmountNumber)
73 States::ParseCurrencyPrefix => {
74 if !read_symbol.is_numeric() {
75 Ok(States::ParseCurrencyPrefix)
77 Ok(States::ParseAmountNumber)
80 States::ParseAmountNumber => {
81 if read_symbol.is_numeric() {
82 Ok(States::ParseAmountNumber)
83 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
84 Ok(States::ParseAmountSiPrefix)
86 Err(super::Bolt11ParseError::UnknownSiPrefix)
89 States::ParseAmountSiPrefix => Err(super::Bolt11ParseError::MalformedHRP),
93 fn is_final(&self) -> bool {
94 !(*self == States::ParseL || *self == States::ParseN)
102 currency_prefix: Option<Range<usize>>,
103 amount_number: Option<Range<usize>>,
104 amount_si_prefix: Option<Range<usize>>,
108 fn new() -> StateMachine {
110 state: States::Start,
112 currency_prefix: None,
114 amount_si_prefix: None,
118 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
119 let new_range = match *range {
120 None => Range {start: position, end: position + 1},
121 Some(ref r) => Range {start: r.start, end: r.end + 1},
123 *range = Some(new_range);
126 fn step(&mut self, c: u8) -> Result<(), super::Bolt11ParseError> {
127 let next_state = self.state.next_state(c)?;
129 States::ParseCurrencyPrefix => {
130 StateMachine::update_range(&mut self.currency_prefix, self.position)
132 States::ParseAmountNumber => {
133 StateMachine::update_range(&mut self.amount_number, self.position)
135 States::ParseAmountSiPrefix => {
136 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
142 self.state = next_state;
146 fn is_final(&self) -> bool {
147 self.state.is_final()
150 fn currency_prefix(&self) -> &Option<Range<usize>> {
151 &self.currency_prefix
154 fn amount_number(&self) -> &Option<Range<usize>> {
158 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
159 &self.amount_si_prefix
163 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> {
164 let mut sm = StateMachine::new();
165 for c in input.bytes() {
170 return Err(super::Bolt11ParseError::MalformedHRP);
173 let currency = sm.currency_prefix().clone()
174 .map(|r| &input[r]).unwrap_or("");
175 let amount = sm.amount_number().clone()
176 .map(|r| &input[r]).unwrap_or("");
177 let si = sm.amount_si_prefix().clone()
178 .map(|r| &input[r]).unwrap_or("");
180 Ok((currency, amount, si))
185 impl FromStr for super::Currency {
186 type Err = Bolt11ParseError;
188 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
189 match currency_prefix {
190 "bc" => Ok(Currency::Bitcoin),
191 "tb" => Ok(Currency::BitcoinTestnet),
192 "bcrt" => Ok(Currency::Regtest),
193 "sb" => Ok(Currency::Simnet),
194 "tbs" => Ok(Currency::Signet),
195 _ => Err(Bolt11ParseError::UnknownCurrency)
200 impl FromStr for SiPrefix {
201 type Err = Bolt11ParseError;
203 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
204 use crate::SiPrefix::*;
205 match currency_prefix {
210 _ => Err(Bolt11ParseError::UnknownSiPrefix)
216 /// use lightning_invoice::Bolt11Invoice;
219 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
220 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
221 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
222 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
223 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
224 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
225 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
226 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
227 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
228 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
229 /// j5r6drg6k6zcqj0fcwg";
231 /// assert!(invoice.parse::<Bolt11Invoice>().is_ok());
233 impl FromStr for Bolt11Invoice {
234 type Err = ParseOrSemanticError;
236 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
237 let signed = s.parse::<SignedRawBolt11Invoice>()?;
238 Ok(Bolt11Invoice::from_signed(signed)?)
243 /// use lightning_invoice::*;
245 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
246 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
247 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
248 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
249 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
250 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
251 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
252 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
253 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
254 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
255 /// j5r6drg6k6zcqj0fcwg";
257 /// let parsed_1 = invoice.parse::<Bolt11Invoice>();
259 /// let parsed_2 = match invoice.parse::<SignedRawBolt11Invoice>() {
260 /// Ok(signed) => match Bolt11Invoice::from_signed(signed) {
261 /// Ok(invoice) => Ok(invoice),
262 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
264 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
267 /// assert!(parsed_1.is_ok());
268 /// assert_eq!(parsed_1, parsed_2);
270 impl FromStr for SignedRawBolt11Invoice {
271 type Err = Bolt11ParseError;
273 fn from_str(s: &str) -> Result<Self, Self::Err> {
274 let (hrp, data, var) = bech32::decode(s)?;
276 if var == bech32::Variant::Bech32m {
277 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
278 // we didn't support Bech32m (which lightning does not use).
279 return Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum));
282 if data.len() < 104 {
283 return Err(Bolt11ParseError::TooShortDataPart);
286 let raw_hrp: RawHrp = hrp.parse()?;
287 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
289 Ok(SignedRawBolt11Invoice {
290 raw_invoice: RawBolt11Invoice {
294 hash: RawBolt11Invoice::hash_from_parts(
296 &data[..data.len()-104]
298 signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
303 impl FromStr for RawHrp {
304 type Err = Bolt11ParseError;
306 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
307 let parts = parse_hrp(hrp)?;
309 let currency = parts.0.parse::<Currency>()?;
311 let amount = if !parts.1.is_empty() {
312 Some(parts.1.parse::<u64>()?)
317 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
320 let si: SiPrefix = parts.2.parse()?;
321 if let Some(amt) = amount {
322 if amt.checked_mul(si.multiplier()).is_none() {
323 return Err(Bolt11ParseError::IntegerOverflowError);
337 impl FromBase32 for RawDataPart {
338 type Err = Bolt11ParseError;
340 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
341 if data.len() < 7 { // timestamp length
342 return Err(Bolt11ParseError::TooShortDataPart);
345 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
346 let tagged = parse_tagged_parts(&data[7..])?;
350 tagged_fields: tagged,
355 impl FromBase32 for PositiveTimestamp {
356 type Err = Bolt11ParseError;
358 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
360 return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
362 let timestamp: u64 = parse_u64_be(b32)
363 .expect("7*5bit < 64bit, no overflow possible");
364 match PositiveTimestamp::from_unix_timestamp(timestamp) {
366 Err(_) => unreachable!(),
371 impl FromBase32 for Bolt11InvoiceSignature {
372 type Err = Bolt11ParseError;
373 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
374 if signature.len() != 104 {
375 return Err(Bolt11ParseError::InvalidSliceLength("Bolt11InvoiceSignature::from_base32()".into()));
377 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
378 let signature = &recoverable_signature_bytes[0..64];
379 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
381 Ok(Bolt11InvoiceSignature(RecoverableSignature::from_compact(
388 macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
389 fn $name(digits: &[u5]) -> Option<$ty> {
390 digits.iter().fold(Some(Default::default()), |acc, b|
392 .and_then(|x| x.checked_mul(32))
393 .and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
397 define_parse_int_be!(parse_u16_be, u16);
398 define_parse_int_be!(parse_u64_be, u64);
400 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
401 let mut parts = Vec::<RawTaggedField>::new();
404 while !data.is_empty() {
406 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
409 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
410 // parse the length to find the end of the tagged field's data
411 let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize;
412 let last_element = 3 + len;
414 if data.len() < last_element {
415 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
418 // Get the tagged field's data slice
419 let field = &data[0..last_element];
421 // Set data slice to remaining data
422 data = &data[last_element..];
424 match TaggedField::from_base32(field) {
426 parts.push(RawTaggedField::KnownSemantics(field))
428 Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
429 parts.push(RawTaggedField::UnknownSemantics(field.into()))
431 Err(e) => {return Err(e)}
437 impl FromBase32 for TaggedField {
438 type Err = Bolt11ParseError;
440 fn from_base32(field: &[u5]) -> Result<TaggedField, Bolt11ParseError> {
442 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
446 let field_data = &field[3..];
449 constants::TAG_PAYMENT_HASH =>
450 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
451 constants::TAG_DESCRIPTION =>
452 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
453 constants::TAG_PAYEE_PUB_KEY =>
454 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
455 constants::TAG_DESCRIPTION_HASH =>
456 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
457 constants::TAG_EXPIRY_TIME =>
458 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
459 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA =>
460 Ok(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta::from_base32(field_data)?)),
461 constants::TAG_FALLBACK =>
462 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
463 constants::TAG_PRIVATE_ROUTE =>
464 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
465 constants::TAG_PAYMENT_SECRET =>
466 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
467 constants::TAG_PAYMENT_METADATA =>
468 Ok(TaggedField::PaymentMetadata(Vec::<u8>::from_base32(field_data)?)),
469 constants::TAG_FEATURES =>
470 Ok(TaggedField::Features(Bolt11InvoiceFeatures::from_base32(field_data)?)),
472 // "A reader MUST skip over unknown fields"
473 Err(Bolt11ParseError::Skip)
479 impl FromBase32 for Sha256 {
480 type Err = Bolt11ParseError;
482 fn from_base32(field_data: &[u5]) -> Result<Sha256, Bolt11ParseError> {
483 if field_data.len() != 52 {
484 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
485 Err(Bolt11ParseError::Skip)
487 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
488 .expect("length was checked before (52 u5 -> 32 u8)")))
493 impl FromBase32 for Description {
494 type Err = Bolt11ParseError;
496 fn from_base32(field_data: &[u5]) -> Result<Description, Bolt11ParseError> {
497 let bytes = Vec::<u8>::from_base32(field_data)?;
498 let description = String::from(str::from_utf8(&bytes)?);
499 Ok(Description::new(description).expect(
500 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
505 impl FromBase32 for PayeePubKey {
506 type Err = Bolt11ParseError;
508 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, Bolt11ParseError> {
509 if field_data.len() != 53 {
510 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
511 Err(Bolt11ParseError::Skip)
513 let data_bytes = Vec::<u8>::from_base32(field_data)?;
514 let pub_key = PublicKey::from_slice(&data_bytes)?;
520 impl FromBase32 for ExpiryTime {
521 type Err = Bolt11ParseError;
523 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, Bolt11ParseError> {
524 match parse_u64_be(field_data)
525 .map(ExpiryTime::from_seconds)
528 None => Err(Bolt11ParseError::IntegerOverflowError),
533 impl FromBase32 for MinFinalCltvExpiryDelta {
534 type Err = Bolt11ParseError;
536 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
537 let expiry = parse_u64_be(field_data);
538 if let Some(expiry) = expiry {
539 Ok(MinFinalCltvExpiryDelta(expiry))
541 Err(Bolt11ParseError::IntegerOverflowError)
546 impl FromBase32 for Fallback {
547 type Err = Bolt11ParseError;
549 fn from_base32(field_data: &[u5]) -> Result<Fallback, Bolt11ParseError> {
550 if field_data.is_empty() {
551 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
554 let version = field_data[0];
555 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
557 match version.to_u8() {
559 if bytes.len() < 2 || bytes.len() > 40 {
560 return Err(Bolt11ParseError::InvalidSegWitProgramLength);
562 let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
563 Ok(Fallback::SegWitProgram {
569 let pkh = match PubkeyHash::from_slice(&bytes) {
571 Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
573 Ok(Fallback::PubKeyHash(pkh))
576 let sh = match ScriptHash::from_slice(&bytes) {
578 Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidScriptHashLength),
580 Ok(Fallback::ScriptHash(sh))
582 _ => Err(Bolt11ParseError::Skip)
587 impl FromBase32 for PrivateRoute {
588 type Err = Bolt11ParseError;
590 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, Bolt11ParseError> {
591 let bytes = Vec::<u8>::from_base32(field_data)?;
593 if bytes.len() % 51 != 0 {
594 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
597 let mut route_hops = Vec::<RouteHintHop>::new();
599 let mut bytes = bytes.as_slice();
600 while !bytes.is_empty() {
601 let hop_bytes = &bytes[0..51];
602 bytes = &bytes[51..];
604 let mut channel_id: [u8; 8] = Default::default();
605 channel_id.copy_from_slice(&hop_bytes[33..41]);
607 let hop = RouteHintHop {
608 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
609 short_channel_id: u64::from_be_bytes(channel_id),
611 base_msat: u32::from_be_bytes(hop_bytes[41..45].try_into().expect("slice too big?")),
612 proportional_millionths: u32::from_be_bytes(hop_bytes[45..49].try_into().expect("slice too big?")),
614 cltv_expiry_delta: u16::from_be_bytes(hop_bytes[49..51].try_into().expect("slice too big?")),
615 htlc_minimum_msat: None,
616 htlc_maximum_msat: None,
619 route_hops.push(hop);
622 Ok(PrivateRoute(RouteHint(route_hops)))
626 impl Display for Bolt11ParseError {
627 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
629 // TODO: find a way to combine the first three arms (e as error::Error?)
630 Bolt11ParseError::Bech32Error(ref e) => {
631 write!(f, "Invalid bech32: {}", e)
633 Bolt11ParseError::ParseAmountError(ref e) => {
634 write!(f, "Invalid amount in hrp ({})", e)
636 Bolt11ParseError::MalformedSignature(ref e) => {
637 write!(f, "Invalid secp256k1 signature: {}", e)
639 Bolt11ParseError::DescriptionDecodeError(ref e) => {
640 write!(f, "Description is not a valid utf-8 string: {}", e)
642 Bolt11ParseError::InvalidSliceLength(ref function) => {
643 write!(f, "Slice in function {} had the wrong length", function)
645 Bolt11ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
646 Bolt11ParseError::UnknownCurrency => f.write_str("currency code unknown"),
647 Bolt11ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
648 Bolt11ParseError::MalformedHRP => f.write_str("malformed human readable part"),
649 Bolt11ParseError::TooShortDataPart => {
650 f.write_str("data part too short (should be at least 111 bech32 chars long)")
652 Bolt11ParseError::UnexpectedEndOfTaggedFields => {
653 f.write_str("tagged fields part ended unexpectedly")
655 Bolt11ParseError::PaddingError => f.write_str("some data field had bad padding"),
656 Bolt11ParseError::IntegerOverflowError => {
657 f.write_str("parsed integer doesn't fit into receiving type")
659 Bolt11ParseError::InvalidSegWitProgramLength => {
660 f.write_str("fallback SegWit program is too long or too short")
662 Bolt11ParseError::InvalidPubKeyHashLength => {
663 f.write_str("fallback public key hash has a length unequal 20 bytes")
665 Bolt11ParseError::InvalidScriptHashLength => {
666 f.write_str("fallback script hash has a length unequal 32 bytes")
668 Bolt11ParseError::InvalidRecoveryId => {
669 f.write_str("recovery id is out of range (should be in [0,3])")
671 Bolt11ParseError::Skip => {
672 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
678 impl Display for ParseOrSemanticError {
679 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
681 ParseOrSemanticError::ParseError(err) => err.fmt(f),
682 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
687 #[cfg(feature = "std")]
688 impl error::Error for Bolt11ParseError {}
690 #[cfg(feature = "std")]
691 impl error::Error for ParseOrSemanticError {}
693 macro_rules! from_error {
694 ($my_error:expr, $extern_error:ty) => {
695 impl From<$extern_error> for Bolt11ParseError {
696 fn from(e: $extern_error) -> Self {
703 from_error!(Bolt11ParseError::MalformedSignature, secp256k1::Error);
704 from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
705 from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);
707 impl From<bech32::Error> for Bolt11ParseError {
708 fn from(e: bech32::Error) -> Self {
710 bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
711 _ => Bolt11ParseError::Bech32Error(e)
716 impl From<Bolt11ParseError> for ParseOrSemanticError {
717 fn from(e: Bolt11ParseError) -> Self {
718 ParseOrSemanticError::ParseError(e)
722 impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
723 fn from(e: Bolt11SemanticError) -> Self {
724 ParseOrSemanticError::SemanticError(e)
730 use crate::de::Bolt11ParseError;
731 use secp256k1::PublicKey;
733 use bitcoin::hashes::sha256;
734 use std::str::FromStr;
736 const CHARSET_REV: [i8; 128] = [
737 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
738 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
739 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
740 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
741 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
742 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
743 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
744 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
747 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
750 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
755 fn test_parse_currency_prefix() {
758 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
759 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
760 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
761 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
762 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
763 assert_eq!("something_else".parse::<Currency>(), Err(Bolt11ParseError::UnknownCurrency))
767 fn test_parse_int_from_bytes_be() {
768 use crate::de::parse_u16_be;
770 assert_eq!(parse_u16_be(&[
771 u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(),
772 u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()]
774 assert_eq!(parse_u16_be(&[
775 u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(),
776 u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()]
781 fn test_parse_sha256_hash() {
783 use bech32::FromBase32;
785 let input = from_bech32(
786 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
789 let hash = sha256::Hash::from_str(
790 "0001020304050607080900010203040506070809000102030405060708090102"
792 let expected = Ok(Sha256(hash));
794 assert_eq!(Sha256::from_base32(&input), expected);
796 // make sure hashes of unknown length get skipped
797 let input_unexpected_length = from_bech32(
798 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
800 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
804 fn test_parse_description() {
805 use crate::Description;
806 use bech32::FromBase32;
808 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
809 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
810 assert_eq!(Description::from_base32(&input), expected);
814 fn test_parse_payee_pub_key() {
815 use crate::PayeePubKey;
816 use bech32::FromBase32;
818 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
820 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
821 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
822 0x0f, 0x93, 0x4d, 0xd9, 0xad
824 let expected = Ok(PayeePubKey(
825 PublicKey::from_slice(&pk_bytes[..]).unwrap()
828 assert_eq!(PayeePubKey::from_base32(&input), expected);
831 let input_unexpected_length = from_bech32(
832 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
834 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
838 fn test_parse_expiry_time() {
839 use crate::ExpiryTime;
840 use bech32::FromBase32;
842 let input = from_bech32("pu".as_bytes());
843 let expected = Ok(ExpiryTime::from_seconds(60));
844 assert_eq!(ExpiryTime::from_base32(&input), expected);
846 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
847 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(Bolt11ParseError::IntegerOverflowError));
851 fn test_parse_min_final_cltv_expiry_delta() {
852 use crate::MinFinalCltvExpiryDelta;
853 use bech32::FromBase32;
855 let input = from_bech32("pr".as_bytes());
856 let expected = Ok(MinFinalCltvExpiryDelta(35));
858 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
862 fn test_parse_fallback() {
864 use bech32::FromBase32;
865 use bitcoin::{PubkeyHash, ScriptHash};
866 use bitcoin::address::WitnessVersion;
867 use bitcoin::hashes::Hash;
871 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
872 Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
873 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
874 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
878 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
879 Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
880 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
881 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
885 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
886 Ok(Fallback::SegWitProgram {
887 version: WitnessVersion::V0,
888 program: Vec::from(&[
889 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
890 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
895 vec![u5::try_from_u8(21).unwrap(); 41],
896 Err(Bolt11ParseError::Skip)
900 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
903 vec![u5::try_from_u8(1).unwrap(); 81],
904 Err(Bolt11ParseError::InvalidSegWitProgramLength)
907 vec![u5::try_from_u8(17).unwrap(); 1],
908 Err(Bolt11ParseError::InvalidPubKeyHashLength)
911 vec![u5::try_from_u8(18).unwrap(); 1],
912 Err(Bolt11ParseError::InvalidScriptHashLength)
916 for (input, expected) in cases.into_iter() {
917 assert_eq!(Fallback::from_base32(&input), expected);
922 fn test_parse_route() {
923 use lightning::routing::gossip::RoutingFees;
924 use lightning::routing::router::{RouteHint, RouteHintHop};
925 use crate::PrivateRoute;
926 use bech32::FromBase32;
928 let input = from_bech32(
929 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
930 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
933 let mut expected = Vec::<RouteHintHop>::new();
934 expected.push(RouteHintHop {
935 src_node_id: PublicKey::from_slice(
937 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
938 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
939 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
942 short_channel_id: 0x0102030405060708,
945 proportional_millionths: 20,
947 cltv_expiry_delta: 3,
948 htlc_minimum_msat: None,
949 htlc_maximum_msat: None
951 expected.push(RouteHintHop {
952 src_node_id: PublicKey::from_slice(
954 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
955 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
956 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
959 short_channel_id: 0x030405060708090a,
962 proportional_millionths: 30,
964 cltv_expiry_delta: 4,
965 htlc_minimum_msat: None,
966 htlc_maximum_msat: None
969 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
972 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
973 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
978 fn test_payment_secret_and_features_de_and_ser() {
979 use lightning::ln::features::Bolt11InvoiceFeatures;
980 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
981 use crate::TaggedField::*;
982 use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart,
983 Currency, Sha256, PositiveTimestamp};
985 // Feature bits 9, 15, and 99 are set.
986 let expected_features = Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
987 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
988 let invoice = SignedRawBolt11Invoice {
989 raw_invoice: RawBolt11Invoice {
991 currency: Currency::Bitcoin,
992 raw_amount: Some(25),
993 si_prefix: Some(SiPrefix::Milli)
996 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
997 tagged_fields: vec ! [
998 PaymentHash(Sha256(sha256::Hash::from_str(
999 "0001020304050607080900010203040506070809000102030405060708090102"
1000 ).unwrap())).into(),
1001 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
1002 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1003 Features(expected_features).into()]}
1005 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1006 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1007 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1008 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1009 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1010 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1011 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1012 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1013 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1014 RecoveryId::from_i32(1).unwrap()
1017 assert_eq!(invoice_str, invoice.to_string());
1019 invoice_str.parse(),
1025 fn test_raw_signed_invoice_deserialization() {
1026 use crate::TaggedField::*;
1027 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
1028 use crate::{SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256,
1032 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1033 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1034 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1035 Ok(SignedRawBolt11Invoice {
1036 raw_invoice: RawBolt11Invoice {
1038 currency: Currency::Bitcoin,
1043 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1044 tagged_fields: vec ! [
1045 PaymentHash(Sha256(sha256::Hash::from_str(
1046 "0001020304050607080900010203040506070809000102030405060708090102"
1047 ).unwrap())).into(),
1049 crate::Description::new(
1050 "Please consider supporting this project".to_owned()
1057 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1058 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1059 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1061 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1063 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1064 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1065 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1066 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1067 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1068 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1070 RecoveryId::from_i32(0).unwrap()