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, WitnessVersion};
14 use bitcoin::hashes::Hash;
15 use bitcoin::hashes::sha256;
16 use crate::prelude::*;
17 use lightning::ln::types::PaymentSecret;
18 use lightning::routing::gossip::RoutingFees;
19 use lightning::routing::router::{RouteHint, RouteHintHop};
21 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
22 use secp256k1::PublicKey;
24 use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
25 Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
26 constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures};
28 use self::hrp_sm::parse_hrp;
30 /// State machine to parse the hrp
34 #[derive(PartialEq, Eq, Debug)]
45 fn next_state(&self, read_byte: u8) -> Result<States, super::Bolt11ParseError> {
46 let read_symbol = match char::from_u32(read_byte.into()) {
47 Some(symb) if symb.is_ascii() => symb,
48 _ => return Err(super::Bolt11ParseError::MalformedHRP),
52 if read_symbol == 'l' {
55 Err(super::Bolt11ParseError::MalformedHRP)
59 if read_symbol == 'n' {
62 Err(super::Bolt11ParseError::MalformedHRP)
66 if !read_symbol.is_numeric() {
67 Ok(States::ParseCurrencyPrefix)
69 Ok(States::ParseAmountNumber)
72 States::ParseCurrencyPrefix => {
73 if !read_symbol.is_numeric() {
74 Ok(States::ParseCurrencyPrefix)
76 Ok(States::ParseAmountNumber)
79 States::ParseAmountNumber => {
80 if read_symbol.is_numeric() {
81 Ok(States::ParseAmountNumber)
82 } else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
83 Ok(States::ParseAmountSiPrefix)
85 Err(super::Bolt11ParseError::UnknownSiPrefix)
88 States::ParseAmountSiPrefix => Err(super::Bolt11ParseError::MalformedHRP),
92 fn is_final(&self) -> bool {
93 !(*self == States::ParseL || *self == States::ParseN)
101 currency_prefix: Option<Range<usize>>,
102 amount_number: Option<Range<usize>>,
103 amount_si_prefix: Option<Range<usize>>,
107 fn new() -> StateMachine {
109 state: States::Start,
111 currency_prefix: None,
113 amount_si_prefix: None,
117 fn update_range(range: &mut Option<Range<usize>>, position: usize) {
118 let new_range = match *range {
119 None => Range {start: position, end: position + 1},
120 Some(ref r) => Range {start: r.start, end: r.end + 1},
122 *range = Some(new_range);
125 fn step(&mut self, c: u8) -> Result<(), super::Bolt11ParseError> {
126 let next_state = self.state.next_state(c)?;
128 States::ParseCurrencyPrefix => {
129 StateMachine::update_range(&mut self.currency_prefix, self.position)
131 States::ParseAmountNumber => {
132 StateMachine::update_range(&mut self.amount_number, self.position)
134 States::ParseAmountSiPrefix => {
135 StateMachine::update_range(&mut self.amount_si_prefix, self.position)
141 self.state = next_state;
145 fn is_final(&self) -> bool {
146 self.state.is_final()
149 fn currency_prefix(&self) -> &Option<Range<usize>> {
150 &self.currency_prefix
153 fn amount_number(&self) -> &Option<Range<usize>> {
157 fn amount_si_prefix(&self) -> &Option<Range<usize>> {
158 &self.amount_si_prefix
162 pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> {
163 let mut sm = StateMachine::new();
164 for c in input.bytes() {
169 return Err(super::Bolt11ParseError::MalformedHRP);
172 let currency = sm.currency_prefix().clone()
173 .map(|r| &input[r]).unwrap_or("");
174 let amount = sm.amount_number().clone()
175 .map(|r| &input[r]).unwrap_or("");
176 let si = sm.amount_si_prefix().clone()
177 .map(|r| &input[r]).unwrap_or("");
179 Ok((currency, amount, si))
184 impl FromStr for super::Currency {
185 type Err = Bolt11ParseError;
187 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
188 match currency_prefix {
189 "bc" => Ok(Currency::Bitcoin),
190 "tb" => Ok(Currency::BitcoinTestnet),
191 "bcrt" => Ok(Currency::Regtest),
192 "sb" => Ok(Currency::Simnet),
193 "tbs" => Ok(Currency::Signet),
194 _ => Err(Bolt11ParseError::UnknownCurrency)
199 impl FromStr for SiPrefix {
200 type Err = Bolt11ParseError;
202 fn from_str(currency_prefix: &str) -> Result<Self, Bolt11ParseError> {
203 use crate::SiPrefix::*;
204 match currency_prefix {
209 _ => Err(Bolt11ParseError::UnknownSiPrefix)
215 /// use lightning_invoice::Bolt11Invoice;
218 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
219 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
220 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
221 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
222 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
223 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
224 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
225 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
226 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
227 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
228 /// j5r6drg6k6zcqj0fcwg";
230 /// assert!(invoice.parse::<Bolt11Invoice>().is_ok());
232 impl FromStr for Bolt11Invoice {
233 type Err = ParseOrSemanticError;
235 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
236 let signed = s.parse::<SignedRawBolt11Invoice>()?;
237 Ok(Bolt11Invoice::from_signed(signed)?)
242 /// use lightning_invoice::*;
244 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
245 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
246 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
247 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
248 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
249 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
250 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
251 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
252 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
253 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
254 /// j5r6drg6k6zcqj0fcwg";
256 /// let parsed_1 = invoice.parse::<Bolt11Invoice>();
258 /// let parsed_2 = match invoice.parse::<SignedRawBolt11Invoice>() {
259 /// Ok(signed) => match Bolt11Invoice::from_signed(signed) {
260 /// Ok(invoice) => Ok(invoice),
261 /// Err(e) => Err(ParseOrSemanticError::SemanticError(e)),
263 /// Err(e) => Err(ParseOrSemanticError::ParseError(e)),
266 /// assert!(parsed_1.is_ok());
267 /// assert_eq!(parsed_1, parsed_2);
269 impl FromStr for SignedRawBolt11Invoice {
270 type Err = Bolt11ParseError;
272 fn from_str(s: &str) -> Result<Self, Self::Err> {
273 let (hrp, data, var) = bech32::decode(s)?;
275 if var == bech32::Variant::Bech32m {
276 // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
277 // we didn't support Bech32m (which lightning does not use).
278 return Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum));
281 if data.len() < 104 {
282 return Err(Bolt11ParseError::TooShortDataPart);
285 let raw_hrp: RawHrp = hrp.parse()?;
286 let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
288 Ok(SignedRawBolt11Invoice {
289 raw_invoice: RawBolt11Invoice {
293 hash: RawBolt11Invoice::hash_from_parts(
295 &data[..data.len()-104]
297 signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
302 impl FromStr for RawHrp {
303 type Err = Bolt11ParseError;
305 fn from_str(hrp: &str) -> Result<Self, <Self as FromStr>::Err> {
306 let parts = parse_hrp(hrp)?;
308 let currency = parts.0.parse::<Currency>()?;
310 let amount = if !parts.1.is_empty() {
311 Some(parts.1.parse::<u64>()?)
316 let si_prefix: Option<SiPrefix> = if parts.2.is_empty() {
319 let si: SiPrefix = parts.2.parse()?;
320 if let Some(amt) = amount {
321 if amt.checked_mul(si.multiplier()).is_none() {
322 return Err(Bolt11ParseError::IntegerOverflowError);
336 impl FromBase32 for RawDataPart {
337 type Err = Bolt11ParseError;
339 fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
340 if data.len() < 7 { // timestamp length
341 return Err(Bolt11ParseError::TooShortDataPart);
344 let timestamp = PositiveTimestamp::from_base32(&data[0..7])?;
345 let tagged = parse_tagged_parts(&data[7..])?;
349 tagged_fields: tagged,
354 impl FromBase32 for PositiveTimestamp {
355 type Err = Bolt11ParseError;
357 fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
359 return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
361 let timestamp: u64 = parse_u64_be(b32)
362 .expect("7*5bit < 64bit, no overflow possible");
363 match PositiveTimestamp::from_unix_timestamp(timestamp) {
365 Err(_) => unreachable!(),
370 impl FromBase32 for Bolt11InvoiceSignature {
371 type Err = Bolt11ParseError;
372 fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
373 if signature.len() != 104 {
374 return Err(Bolt11ParseError::InvalidSliceLength("Bolt11InvoiceSignature::from_base32()".into()));
376 let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
377 let signature = &recoverable_signature_bytes[0..64];
378 let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
380 Ok(Bolt11InvoiceSignature(RecoverableSignature::from_compact(
387 macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
388 fn $name(digits: &[u5]) -> Option<$ty> {
389 digits.iter().fold(Some(Default::default()), |acc, b|
391 .and_then(|x| x.checked_mul(32))
392 .and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
396 define_parse_int_be!(parse_u16_be, u16);
397 define_parse_int_be!(parse_u64_be, u64);
399 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
400 let mut parts = Vec::<RawTaggedField>::new();
403 while !data.is_empty() {
405 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
408 // Ignore tag at data[0], it will be handled in the TaggedField parsers and
409 // parse the length to find the end of the tagged field's data
410 let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize;
411 let last_element = 3 + len;
413 if data.len() < last_element {
414 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
417 // Get the tagged field's data slice
418 let field = &data[0..last_element];
420 // Set data slice to remaining data
421 data = &data[last_element..];
423 match TaggedField::from_base32(field) {
425 parts.push(RawTaggedField::KnownSemantics(field))
427 Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
428 parts.push(RawTaggedField::UnknownSemantics(field.into()))
430 Err(e) => {return Err(e)}
436 impl FromBase32 for TaggedField {
437 type Err = Bolt11ParseError;
439 fn from_base32(field: &[u5]) -> Result<TaggedField, Bolt11ParseError> {
441 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
445 let field_data = &field[3..];
448 constants::TAG_PAYMENT_HASH =>
449 Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
450 constants::TAG_DESCRIPTION =>
451 Ok(TaggedField::Description(Description::from_base32(field_data)?)),
452 constants::TAG_PAYEE_PUB_KEY =>
453 Ok(TaggedField::PayeePubKey(PayeePubKey::from_base32(field_data)?)),
454 constants::TAG_DESCRIPTION_HASH =>
455 Ok(TaggedField::DescriptionHash(Sha256::from_base32(field_data)?)),
456 constants::TAG_EXPIRY_TIME =>
457 Ok(TaggedField::ExpiryTime(ExpiryTime::from_base32(field_data)?)),
458 constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA =>
459 Ok(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta::from_base32(field_data)?)),
460 constants::TAG_FALLBACK =>
461 Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
462 constants::TAG_PRIVATE_ROUTE =>
463 Ok(TaggedField::PrivateRoute(PrivateRoute::from_base32(field_data)?)),
464 constants::TAG_PAYMENT_SECRET =>
465 Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
466 constants::TAG_PAYMENT_METADATA =>
467 Ok(TaggedField::PaymentMetadata(Vec::<u8>::from_base32(field_data)?)),
468 constants::TAG_FEATURES =>
469 Ok(TaggedField::Features(Bolt11InvoiceFeatures::from_base32(field_data)?)),
471 // "A reader MUST skip over unknown fields"
472 Err(Bolt11ParseError::Skip)
478 impl FromBase32 for Sha256 {
479 type Err = Bolt11ParseError;
481 fn from_base32(field_data: &[u5]) -> Result<Sha256, Bolt11ParseError> {
482 if field_data.len() != 52 {
483 // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
484 Err(Bolt11ParseError::Skip)
486 Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
487 .expect("length was checked before (52 u5 -> 32 u8)")))
492 impl FromBase32 for Description {
493 type Err = Bolt11ParseError;
495 fn from_base32(field_data: &[u5]) -> Result<Description, Bolt11ParseError> {
496 let bytes = Vec::<u8>::from_base32(field_data)?;
497 let description = String::from(str::from_utf8(&bytes)?);
498 Ok(Description::new(description).expect(
499 "Max len is 639=floor(1023*5/8) since the len field is only 10bits long"
504 impl FromBase32 for PayeePubKey {
505 type Err = Bolt11ParseError;
507 fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, Bolt11ParseError> {
508 if field_data.len() != 53 {
509 // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
510 Err(Bolt11ParseError::Skip)
512 let data_bytes = Vec::<u8>::from_base32(field_data)?;
513 let pub_key = PublicKey::from_slice(&data_bytes)?;
519 impl FromBase32 for ExpiryTime {
520 type Err = Bolt11ParseError;
522 fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, Bolt11ParseError> {
523 match parse_u64_be(field_data)
524 .map(ExpiryTime::from_seconds)
527 None => Err(Bolt11ParseError::IntegerOverflowError),
532 impl FromBase32 for MinFinalCltvExpiryDelta {
533 type Err = Bolt11ParseError;
535 fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
536 let expiry = parse_u64_be(field_data);
537 if let Some(expiry) = expiry {
538 Ok(MinFinalCltvExpiryDelta(expiry))
540 Err(Bolt11ParseError::IntegerOverflowError)
545 impl FromBase32 for Fallback {
546 type Err = Bolt11ParseError;
548 fn from_base32(field_data: &[u5]) -> Result<Fallback, Bolt11ParseError> {
549 if field_data.is_empty() {
550 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
553 let version = field_data[0].to_u8();
554 let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
558 if bytes.len() < 2 || bytes.len() > 40 {
559 return Err(Bolt11ParseError::InvalidSegWitProgramLength);
561 let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
562 Ok(Fallback::SegWitProgram {
568 let pkh = match PubkeyHash::from_slice(&bytes) {
570 Err(_) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
572 Ok(Fallback::PubKeyHash(pkh))
575 let sh = match ScriptHash::from_slice(&bytes) {
577 Err(_) => return Err(Bolt11ParseError::InvalidScriptHashLength),
579 Ok(Fallback::ScriptHash(sh))
581 _ => Err(Bolt11ParseError::Skip)
586 impl FromBase32 for PrivateRoute {
587 type Err = Bolt11ParseError;
589 fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, Bolt11ParseError> {
590 let bytes = Vec::<u8>::from_base32(field_data)?;
592 if bytes.len() % 51 != 0 {
593 return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
596 let mut route_hops = Vec::<RouteHintHop>::new();
598 let mut bytes = bytes.as_slice();
599 while !bytes.is_empty() {
600 let hop_bytes = &bytes[0..51];
601 bytes = &bytes[51..];
603 let mut channel_id: [u8; 8] = Default::default();
604 channel_id.copy_from_slice(&hop_bytes[33..41]);
606 let hop = RouteHintHop {
607 src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
608 short_channel_id: u64::from_be_bytes(channel_id),
610 base_msat: u32::from_be_bytes(hop_bytes[41..45].try_into().expect("slice too big?")),
611 proportional_millionths: u32::from_be_bytes(hop_bytes[45..49].try_into().expect("slice too big?")),
613 cltv_expiry_delta: u16::from_be_bytes(hop_bytes[49..51].try_into().expect("slice too big?")),
614 htlc_minimum_msat: None,
615 htlc_maximum_msat: None,
618 route_hops.push(hop);
621 Ok(PrivateRoute(RouteHint(route_hops)))
625 impl Display for Bolt11ParseError {
626 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
628 // TODO: find a way to combine the first three arms (e as error::Error?)
629 Bolt11ParseError::Bech32Error(ref e) => {
630 write!(f, "Invalid bech32: {}", e)
632 Bolt11ParseError::ParseAmountError(ref e) => {
633 write!(f, "Invalid amount in hrp ({})", e)
635 Bolt11ParseError::MalformedSignature(ref e) => {
636 write!(f, "Invalid secp256k1 signature: {}", e)
638 Bolt11ParseError::DescriptionDecodeError(ref e) => {
639 write!(f, "Description is not a valid utf-8 string: {}", e)
641 Bolt11ParseError::InvalidSliceLength(ref function) => {
642 write!(f, "Slice in function {} had the wrong length", function)
644 Bolt11ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
645 Bolt11ParseError::UnknownCurrency => f.write_str("currency code unknown"),
646 Bolt11ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
647 Bolt11ParseError::MalformedHRP => f.write_str("malformed human readable part"),
648 Bolt11ParseError::TooShortDataPart => {
649 f.write_str("data part too short (should be at least 111 bech32 chars long)")
651 Bolt11ParseError::UnexpectedEndOfTaggedFields => {
652 f.write_str("tagged fields part ended unexpectedly")
654 Bolt11ParseError::PaddingError => f.write_str("some data field had bad padding"),
655 Bolt11ParseError::IntegerOverflowError => {
656 f.write_str("parsed integer doesn't fit into receiving type")
658 Bolt11ParseError::InvalidSegWitProgramLength => {
659 f.write_str("fallback SegWit program is too long or too short")
661 Bolt11ParseError::InvalidPubKeyHashLength => {
662 f.write_str("fallback public key hash has a length unequal 20 bytes")
664 Bolt11ParseError::InvalidScriptHashLength => {
665 f.write_str("fallback script hash has a length unequal 32 bytes")
667 Bolt11ParseError::InvalidRecoveryId => {
668 f.write_str("recovery id is out of range (should be in [0,3])")
670 Bolt11ParseError::Skip => {
671 f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
677 impl Display for ParseOrSemanticError {
678 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
680 ParseOrSemanticError::ParseError(err) => err.fmt(f),
681 ParseOrSemanticError::SemanticError(err) => err.fmt(f),
686 #[cfg(feature = "std")]
687 impl error::Error for Bolt11ParseError {}
689 #[cfg(feature = "std")]
690 impl error::Error for ParseOrSemanticError {}
692 macro_rules! from_error {
693 ($my_error:expr, $extern_error:ty) => {
694 impl From<$extern_error> for Bolt11ParseError {
695 fn from(e: $extern_error) -> Self {
702 from_error!(Bolt11ParseError::MalformedSignature, secp256k1::Error);
703 from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
704 from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);
706 impl From<bech32::Error> for Bolt11ParseError {
707 fn from(e: bech32::Error) -> Self {
709 bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
710 _ => Bolt11ParseError::Bech32Error(e)
715 impl From<Bolt11ParseError> for ParseOrSemanticError {
716 fn from(e: Bolt11ParseError) -> Self {
717 ParseOrSemanticError::ParseError(e)
721 impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
722 fn from(e: Bolt11SemanticError) -> Self {
723 ParseOrSemanticError::SemanticError(e)
729 use crate::de::Bolt11ParseError;
730 use secp256k1::PublicKey;
732 use bitcoin::hashes::sha256;
733 use std::str::FromStr;
735 const CHARSET_REV: [i8; 128] = [
736 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
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 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
740 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
741 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
742 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
743 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
746 fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
749 .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
754 fn test_parse_currency_prefix() {
757 assert_eq!("bc".parse::<Currency>(), Ok(Currency::Bitcoin));
758 assert_eq!("tb".parse::<Currency>(), Ok(Currency::BitcoinTestnet));
759 assert_eq!("bcrt".parse::<Currency>(), Ok(Currency::Regtest));
760 assert_eq!("sb".parse::<Currency>(), Ok(Currency::Simnet));
761 assert_eq!("tbs".parse::<Currency>(), Ok(Currency::Signet));
762 assert_eq!("something_else".parse::<Currency>(), Err(Bolt11ParseError::UnknownCurrency))
766 fn test_parse_int_from_bytes_be() {
767 use crate::de::parse_u16_be;
769 assert_eq!(parse_u16_be(&[
770 u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(),
771 u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()]
773 assert_eq!(parse_u16_be(&[
774 u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(),
775 u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()]
780 fn test_parse_sha256_hash() {
782 use bech32::FromBase32;
784 let input = from_bech32(
785 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
788 let hash = sha256::Hash::from_str(
789 "0001020304050607080900010203040506070809000102030405060708090102"
791 let expected = Ok(Sha256(hash));
793 assert_eq!(Sha256::from_base32(&input), expected);
795 // make sure hashes of unknown length get skipped
796 let input_unexpected_length = from_bech32(
797 "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypyq".as_bytes()
799 assert_eq!(Sha256::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
803 fn test_parse_description() {
804 use crate::Description;
805 use bech32::FromBase32;
807 let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
808 let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
809 assert_eq!(Description::from_base32(&input), expected);
813 fn test_parse_payee_pub_key() {
814 use crate::PayeePubKey;
815 use bech32::FromBase32;
817 let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
819 0x03, 0xe7, 0x15, 0x6a, 0xe3, 0x3b, 0x0a, 0x20, 0x8d, 0x07, 0x44, 0x19, 0x91, 0x63,
820 0x17, 0x7e, 0x90, 0x9e, 0x80, 0x17, 0x6e, 0x55, 0xd9, 0x7a, 0x2f, 0x22, 0x1e, 0xde,
821 0x0f, 0x93, 0x4d, 0xd9, 0xad
823 let expected = Ok(PayeePubKey(
824 PublicKey::from_slice(&pk_bytes[..]).unwrap()
827 assert_eq!(PayeePubKey::from_base32(&input), expected);
830 let input_unexpected_length = from_bech32(
831 "q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhvq".as_bytes()
833 assert_eq!(PayeePubKey::from_base32(&input_unexpected_length), Err(Bolt11ParseError::Skip));
837 fn test_parse_expiry_time() {
838 use crate::ExpiryTime;
839 use bech32::FromBase32;
841 let input = from_bech32("pu".as_bytes());
842 let expected = Ok(ExpiryTime::from_seconds(60));
843 assert_eq!(ExpiryTime::from_base32(&input), expected);
845 let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
846 assert_eq!(ExpiryTime::from_base32(&input_too_large), Err(Bolt11ParseError::IntegerOverflowError));
850 fn test_parse_min_final_cltv_expiry_delta() {
851 use crate::MinFinalCltvExpiryDelta;
852 use bech32::FromBase32;
854 let input = from_bech32("pr".as_bytes());
855 let expected = Ok(MinFinalCltvExpiryDelta(35));
857 assert_eq!(MinFinalCltvExpiryDelta::from_base32(&input), expected);
861 fn test_parse_fallback() {
863 use bech32::FromBase32;
864 use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
865 use bitcoin::hashes::Hash;
869 from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
870 Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
871 0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
872 0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
876 from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
877 Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
878 0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
879 0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
883 from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
884 Ok(Fallback::SegWitProgram {
885 version: WitnessVersion::V0,
886 program: Vec::from(&[
887 0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
888 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
893 vec![u5::try_from_u8(21).unwrap(); 41],
894 Err(Bolt11ParseError::Skip)
898 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
901 vec![u5::try_from_u8(1).unwrap(); 81],
902 Err(Bolt11ParseError::InvalidSegWitProgramLength)
905 vec![u5::try_from_u8(17).unwrap(); 1],
906 Err(Bolt11ParseError::InvalidPubKeyHashLength)
909 vec![u5::try_from_u8(18).unwrap(); 1],
910 Err(Bolt11ParseError::InvalidScriptHashLength)
914 for (input, expected) in cases.into_iter() {
915 assert_eq!(Fallback::from_base32(&input), expected);
920 fn test_parse_route() {
921 use lightning::routing::gossip::RoutingFees;
922 use lightning::routing::router::{RouteHint, RouteHintHop};
923 use crate::PrivateRoute;
924 use bech32::FromBase32;
926 let input = from_bech32(
927 "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
928 fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
931 let mut expected = Vec::<RouteHintHop>::new();
932 expected.push(RouteHintHop {
933 src_node_id: PublicKey::from_slice(
935 0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
936 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
937 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
940 short_channel_id: 0x0102030405060708,
943 proportional_millionths: 20,
945 cltv_expiry_delta: 3,
946 htlc_minimum_msat: None,
947 htlc_maximum_msat: None
949 expected.push(RouteHintHop {
950 src_node_id: PublicKey::from_slice(
952 0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
953 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
954 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
957 short_channel_id: 0x030405060708090a,
960 proportional_millionths: 30,
962 cltv_expiry_delta: 4,
963 htlc_minimum_msat: None,
964 htlc_maximum_msat: None
967 assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
970 PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
971 Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
976 fn test_payment_secret_and_features_de_and_ser() {
977 use lightning::ln::features::Bolt11InvoiceFeatures;
978 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
979 use crate::TaggedField::*;
980 use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart,
981 Currency, Sha256, PositiveTimestamp};
983 // Feature bits 9, 15, and 99 are set.
984 let expected_features = Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
985 let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
986 let invoice = SignedRawBolt11Invoice {
987 raw_invoice: RawBolt11Invoice {
989 currency: Currency::Bitcoin,
990 raw_amount: Some(25),
991 si_prefix: Some(SiPrefix::Milli)
994 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
995 tagged_fields: vec ! [
996 PaymentHash(Sha256(sha256::Hash::from_str(
997 "0001020304050607080900010203040506070809000102030405060708090102"
999 Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
1000 PaymentSecret(crate::PaymentSecret([17; 32])).into(),
1001 Features(expected_features).into()]}
1003 hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1004 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1005 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9],
1006 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1007 &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68,
1008 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60,
1009 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3,
1010 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08, 0x12, 0xf9, 0x5d, 0x97,
1011 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda, 0xe0, 0x1a, 0xf3, 0xc1],
1012 RecoveryId::from_i32(1).unwrap()
1015 assert_eq!(invoice_str, invoice.to_string());
1017 invoice_str.parse(),
1023 fn test_raw_signed_invoice_deserialization() {
1024 use crate::TaggedField::*;
1025 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
1026 use crate::{SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256,
1030 "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
1031 wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
1032 ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".parse(),
1033 Ok(SignedRawBolt11Invoice {
1034 raw_invoice: RawBolt11Invoice {
1036 currency: Currency::Bitcoin,
1041 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1042 tagged_fields: vec ! [
1043 PaymentHash(Sha256(sha256::Hash::from_str(
1044 "0001020304050607080900010203040506070809000102030405060708090102"
1045 ).unwrap())).into(),
1047 crate::Description::new(
1048 "Please consider supporting this project".to_owned()
1055 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1056 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1057 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1059 signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact(
1061 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1062 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1063 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1064 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1065 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1066 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1068 RecoveryId::from_i32(0).unwrap()