2 #![deny(non_upper_case_globals)]
3 #![deny(non_camel_case_types)]
4 #![deny(non_snake_case)]
6 #![deny(broken_intra_doc_links)]
8 #![cfg_attr(feature = "strict", deny(warnings))]
10 //! This crate provides data structures to represent
11 //! [lightning BOLT11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md)
12 //! invoices and functions to create, encode and decode these. If you just want to use the standard
13 //! en-/decoding functionality this should get you started:
15 //! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
16 //! * For constructing invoices use the `InvoiceBuilder`
17 //! * For serializing invoices use the `Display`/`ToString` traits
21 extern crate bitcoin_hashes;
22 extern crate lightning;
23 extern crate num_traits;
24 extern crate secp256k1;
27 use bitcoin_hashes::Hash;
28 use bitcoin_hashes::sha256;
29 use lightning::ln::PaymentSecret;
30 use lightning::ln::features::InvoiceFeatures;
31 #[cfg(any(doc, test))]
32 use lightning::routing::network_graph::RoutingFees;
33 use lightning::routing::router::RouteHintHop;
35 use secp256k1::key::PublicKey;
36 use secp256k1::{Message, Secp256k1};
37 use secp256k1::recovery::RecoverableSignature;
39 use std::fmt::{Display, Formatter, self};
40 use std::iter::FilterMap;
43 use std::time::{SystemTime, Duration, UNIX_EPOCH};
49 pub use de::{ParseError, ParseOrSemanticError};
51 // TODO: fix before 2037 (see rust PR #55527)
52 /// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
53 /// one of the unit tests, please run them.
54 const SYSTEM_TIME_MAX_UNIX_TIMESTAMP: u64 = std::i32::MAX as u64;
56 /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
57 /// it should be rather low as long as we still have to support 32bit time representations
58 const MAX_EXPIRY_TIME: u64 = 60 * 60 * 24 * 356;
60 /// Default expiry time as defined by [BOLT 11].
62 /// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
63 const DEFAULT_EXPIRY_TIME: u64 = 3600;
65 /// Default minimum final CLTV expiry as defined by [BOLT 11].
67 /// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
68 const DEFAULT_MIN_FINAL_CLTV_EXPIRY: u64 = 18;
70 /// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
71 /// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
72 /// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
73 /// please open an issue. If all tests pass you should be able to use this library safely by just
74 /// removing this function till we patch it accordingly.
75 fn __system_time_size_check() {
76 // Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
77 // a `Duration` since `SystemTime::UNIX_EPOCH`.
78 unsafe { std::mem::transmute_copy::<SystemTime, [u8; 16]>(&UNIX_EPOCH); }
82 /// **Call this function on startup to ensure that all assumptions about the platform are valid.**
84 /// Unfortunately we have to make assumptions about the upper bounds of the `SystemTime` type on
85 /// your platform which we can't fully verify at compile time and which isn't part of it's contract.
86 /// To our best knowledge our assumptions hold for all platforms officially supported by rust, but
87 /// since this check is fast we recommend to do it anyway.
89 /// If this function fails this is considered a bug. Please open an issue describing your
90 /// platform and stating your current system time.
93 /// If the check fails this function panics. By calling this function on startup you ensure that
94 /// this wont happen at an arbitrary later point in time.
95 pub fn check_platform() {
96 // The upper and lower bounds of `SystemTime` are not part of its public contract and are
97 // platform specific. That's why we have to test if our assumptions regarding these bounds
98 // hold on the target platform.
100 // If this test fails on your platform, please don't use the library and open an issue
101 // instead so we can resolve the situation. Currently this library is tested on:
103 let fail_date = UNIX_EPOCH + Duration::from_secs(SYSTEM_TIME_MAX_UNIX_TIMESTAMP);
104 let year = Duration::from_secs(60 * 60 * 24 * 365);
106 // Make sure that the library will keep working for another year
107 assert!(fail_date.duration_since(SystemTime::now()).unwrap() > year);
109 let max_ts = PositiveTimestamp::from_unix_timestamp(
110 SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
112 let max_exp = ::ExpiryTime::from_seconds(MAX_EXPIRY_TIME).unwrap();
115 (*max_ts.as_time() + *max_exp.as_duration()).duration_since(UNIX_EPOCH).unwrap().as_secs(),
116 SYSTEM_TIME_MAX_UNIX_TIMESTAMP
121 /// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
122 /// that only a semantically and syntactically correct Invoice can be built using it.
125 /// extern crate secp256k1;
126 /// extern crate lightning_invoice;
127 /// extern crate bitcoin_hashes;
129 /// use bitcoin_hashes::Hash;
130 /// use bitcoin_hashes::sha256;
132 /// use secp256k1::Secp256k1;
133 /// use secp256k1::key::SecretKey;
135 /// use lightning_invoice::{Currency, InvoiceBuilder};
138 /// let private_key = SecretKey::from_slice(
140 /// 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f,
141 /// 0xe2, 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04,
142 /// 0xa8, 0xca, 0x3b, 0x2d, 0xb7, 0x34
146 /// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
148 /// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
149 /// .description("Coins pls!".into())
150 /// .payment_hash(payment_hash)
151 /// .current_timestamp()
152 /// .min_final_cltv_expiry(144)
153 /// .build_signed(|hash| {
154 /// Secp256k1::new().sign_recoverable(hash, &private_key)
158 /// assert!(invoice.to_string().starts_with("lnbc1"));
162 /// # Type parameters
163 /// The two parameters `D` and `H` signal if the builder already contains the correct amount of the
165 /// * `D`: exactly one `Description` or `DescriptionHash`
166 /// * `H`: exactly one `PaymentHash`
167 /// * `T`: the timestamp is set
169 /// (C-not exported) as we likely need to manually select one set of boolean type parameters.
170 #[derive(Eq, PartialEq, Debug, Clone)]
171 pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> {
174 si_prefix: Option<SiPrefix>,
175 timestamp: Option<PositiveTimestamp>,
176 tagged_fields: Vec<TaggedField>,
177 error: Option<CreationError>,
179 phantom_d: std::marker::PhantomData<D>,
180 phantom_h: std::marker::PhantomData<H>,
181 phantom_t: std::marker::PhantomData<T>,
182 phantom_c: std::marker::PhantomData<C>,
185 /// Represents a syntactically and semantically correct lightning BOLT11 invoice.
187 /// There are three ways to construct an `Invoice`:
188 /// 1. using `InvoiceBuilder`
189 /// 2. using `Invoice::from_signed(SignedRawInvoice)`
190 /// 3. using `str::parse::<Invoice>(&str)`
191 #[derive(Eq, PartialEq, Debug, Clone)]
193 signed_invoice: SignedRawInvoice,
196 /// Represents the description of an invoice which has to be either a directly included string or
197 /// a hash of a description provided out of band.
199 /// (C-not exported) As we don't have a good way to map the reference lifetimes making this
200 /// practically impossible to use safely in languages like C.
201 #[derive(Eq, PartialEq, Debug, Clone)]
202 pub enum InvoiceDescription<'f> {
203 /// Reference to the directly supplied description in the invoice
204 Direct(&'f Description),
206 /// Reference to the description's hash included in the invoice
210 /// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be
214 /// The hash has to be either from the deserialized invoice or from the serialized `raw_invoice`.
215 #[derive(Eq, PartialEq, Debug, Clone)]
216 pub struct SignedRawInvoice {
217 /// The rawInvoice that the signature belongs to
218 raw_invoice: RawInvoice,
220 /// Hash of the `RawInvoice` that will be used to check the signature.
222 /// * if the `SignedRawInvoice` was deserialized the hash is of from the original encoded form,
223 /// since it's not guaranteed that encoding it again will lead to the same result since integers
224 /// could have been encoded with leading zeroes etc.
225 /// * if the `SignedRawInvoice` was constructed manually the hash will be the calculated hash
226 /// from the `RawInvoice`
229 /// signature of the payment request
230 signature: InvoiceSignature,
233 /// Represents an syntactically correct Invoice for a payment on the lightning network,
234 /// but without the signature information.
235 /// De- and encoding should not lead to information loss but may lead to different hashes.
237 /// For methods without docs see the corresponding methods in `Invoice`.
238 #[derive(Eq, PartialEq, Debug, Clone)]
239 pub struct RawInvoice {
240 /// human readable part
244 pub data: RawDataPart,
247 /// Data of the `RawInvoice` that is encoded in the human readable part
249 /// (C-not exported) As we don't yet support Option<Enum>
250 #[derive(Eq, PartialEq, Debug, Clone)]
252 /// The currency deferred from the 3rd and 4th character of the bech32 transaction
253 pub currency: Currency,
255 /// The amount that, multiplied by the SI prefix, has to be payed
256 pub raw_amount: Option<u64>,
258 /// SI prefix that gets multiplied with the `raw_amount`
259 pub si_prefix: Option<SiPrefix>,
262 /// Data of the `RawInvoice` that is encoded in the data part
263 #[derive(Eq, PartialEq, Debug, Clone)]
264 pub struct RawDataPart {
265 /// generation time of the invoice
266 pub timestamp: PositiveTimestamp,
268 /// tagged fields of the payment request
269 pub tagged_fields: Vec<RawTaggedField>,
272 /// A timestamp that refers to a date after 1 January 1970 which means its representation as UNIX
273 /// timestamp is positive.
276 /// The UNIX timestamp representing the stored time has to be positive and small enough so that
277 /// a `EpiryTime` can be added to it without an overflow.
278 #[derive(Eq, PartialEq, Debug, Clone)]
279 pub struct PositiveTimestamp(SystemTime);
281 /// SI prefixes for the human readable part
282 #[derive(Eq, PartialEq, Debug, Clone, Copy)]
295 /// Returns the multiplier to go from a BTC value to picoBTC implied by this SiPrefix.
296 /// This is effectively 10^12 * the prefix multiplier
297 pub fn multiplier(&self) -> u64 {
299 SiPrefix::Milli => 1_000_000_000,
300 SiPrefix::Micro => 1_000_000,
301 SiPrefix::Nano => 1_000,
306 /// Returns all enum variants of `SiPrefix` sorted in descending order of their associated
309 /// (C-not exported) As we don't yet support a slice of enums, and also because this function
310 /// isn't the most critical to expose.
311 pub fn values_desc() -> &'static [SiPrefix] {
313 static VALUES: [SiPrefix; 4] = [Milli, Micro, Nano, Pico];
318 /// Enum representing the crypto currencies (or networks) supported by this library
319 #[derive(Eq, PartialEq, Debug, Clone)]
330 /// Bitcoin simnet/signet
334 /// Tagged field which may have an unknown tag
336 /// (C-not exported) as we don't currently support TaggedField
337 #[derive(Eq, PartialEq, Debug, Clone)]
338 pub enum RawTaggedField {
339 /// Parsed tagged field with known tag
340 KnownSemantics(TaggedField),
341 /// tagged field which was not parsed due to an unknown tag or undefined field semantics
342 UnknownSemantics(Vec<u5>),
345 /// Tagged field with known tag
347 /// For descriptions of the enum values please refer to the enclosed type's docs.
349 /// (C-not exported) As we don't yet support enum variants with the same name the struct contained
351 #[allow(missing_docs)]
352 #[derive(Eq, PartialEq, Debug, Clone)]
353 pub enum TaggedField {
355 Description(Description),
356 PayeePubKey(PayeePubKey),
357 DescriptionHash(Sha256),
358 ExpiryTime(ExpiryTime),
359 MinFinalCltvExpiry(MinFinalCltvExpiry),
362 PaymentSecret(PaymentSecret),
363 Features(InvoiceFeatures),
367 #[derive(Eq, PartialEq, Debug, Clone)]
368 pub struct Sha256(pub sha256::Hash);
370 /// Description string
373 /// The description can be at most 639 __bytes__ long
374 #[derive(Eq, PartialEq, Debug, Clone)]
375 pub struct Description(String);
378 #[derive(Eq, PartialEq, Debug, Clone)]
379 pub struct PayeePubKey(pub PublicKey);
381 /// Positive duration that defines when (relatively to the timestamp) in the future the invoice
385 /// The number of seconds this expiry time represents has to be in the range
386 /// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
388 #[derive(Eq, PartialEq, Debug, Clone)]
389 pub struct ExpiryTime(Duration);
391 /// `min_final_cltv_expiry` to use for the last HTLC in the route
392 #[derive(Eq, PartialEq, Debug, Clone)]
393 pub struct MinFinalCltvExpiry(pub u64);
395 // TODO: better types instead onf byte arrays
396 /// Fallback address in case no LN payment is possible
397 #[allow(missing_docs)]
398 #[derive(Eq, PartialEq, Debug, Clone)]
404 PubKeyHash([u8; 20]),
405 ScriptHash([u8; 20]),
408 /// Recoverable signature
409 #[derive(Eq, PartialEq, Debug, Clone)]
410 pub struct InvoiceSignature(pub RecoverableSignature);
412 /// Private routing information
415 /// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
417 #[derive(Eq, PartialEq, Debug, Clone)]
418 pub struct RouteHint(Vec<RouteHintHop>);
420 /// Tag constants as specified in BOLT11
421 #[allow(missing_docs)]
423 pub const TAG_PAYMENT_HASH: u8 = 1;
424 pub const TAG_DESCRIPTION: u8 = 13;
425 pub const TAG_PAYEE_PUB_KEY: u8 = 19;
426 pub const TAG_DESCRIPTION_HASH: u8 = 23;
427 pub const TAG_EXPIRY_TIME: u8 = 6;
428 pub const TAG_MIN_FINAL_CLTV_EXPIRY: u8 = 24;
429 pub const TAG_FALLBACK: u8 = 9;
430 pub const TAG_ROUTE: u8 = 3;
431 pub const TAG_PAYMENT_SECRET: u8 = 16;
432 pub const TAG_FEATURES: u8 = 5;
435 impl InvoiceBuilder<tb::False, tb::False, tb::False, tb::False> {
436 /// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
437 /// `InvoiceBuilder::build(self)` becomes available.
438 pub fn new(currrency: Currency) -> Self {
444 tagged_fields: Vec::new(),
447 phantom_d: std::marker::PhantomData,
448 phantom_h: std::marker::PhantomData,
449 phantom_t: std::marker::PhantomData,
450 phantom_c: std::marker::PhantomData,
455 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C> {
456 /// Helper function to set the completeness flags.
457 fn set_flags<DN: tb::Bool, HN: tb::Bool, TN: tb::Bool, CN: tb::Bool>(self) -> InvoiceBuilder<DN, HN, TN, CN> {
458 InvoiceBuilder::<DN, HN, TN, CN> {
459 currency: self.currency,
461 si_prefix: self.si_prefix,
462 timestamp: self.timestamp,
463 tagged_fields: self.tagged_fields,
466 phantom_d: std::marker::PhantomData,
467 phantom_h: std::marker::PhantomData,
468 phantom_t: std::marker::PhantomData,
469 phantom_c: std::marker::PhantomData,
473 /// Sets the amount in pico BTC. The optimal SI prefix is choosen automatically.
474 pub fn amount_pico_btc(mut self, amount: u64) -> Self {
475 let biggest_possible_si_prefix = SiPrefix::values_desc()
477 .find(|prefix| amount % prefix.multiplier() == 0)
478 .expect("Pico should always match");
479 self.amount = Some(amount / biggest_possible_si_prefix.multiplier());
480 self.si_prefix = Some(*biggest_possible_si_prefix);
484 /// Sets the payee's public key.
485 pub fn payee_pub_key(mut self, pub_key: PublicKey) -> Self {
486 self.tagged_fields.push(TaggedField::PayeePubKey(PayeePubKey(pub_key)));
490 /// Sets the payment secret
491 pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> Self {
492 self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret));
496 /// Sets the expiry time
497 pub fn expiry_time(mut self, expiry_time: Duration) -> Self {
498 match ExpiryTime::from_duration(expiry_time) {
499 Ok(t) => self.tagged_fields.push(TaggedField::ExpiryTime(t)),
500 Err(e) => self.error = Some(e),
505 /// Adds a fallback address.
506 pub fn fallback(mut self, fallback: Fallback) -> Self {
507 self.tagged_fields.push(TaggedField::Fallback(fallback));
511 /// Adds a private route.
512 pub fn route(mut self, route: Vec<RouteHintHop>) -> Self {
513 match RouteHint::new(route) {
514 Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
515 Err(e) => self.error = Some(e),
520 /// Adds a features field which indicates the set of supported protocol extensions which the
521 /// origin node supports.
522 pub fn features(mut self, features: InvoiceFeatures) -> Self {
523 self.tagged_fields.push(TaggedField::Features(features));
528 impl<D: tb::Bool, H: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, tb::True, C> {
529 /// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields.
530 pub fn build_raw(self) -> Result<RawInvoice, CreationError> {
532 // If an error occurred at any time before, return it now
533 if let Some(e) = self.error {
538 currency: self.currency,
539 raw_amount: self.amount,
540 si_prefix: self.si_prefix,
543 let timestamp = self.timestamp.expect("ensured to be Some(t) by type T");
545 let tagged_fields = self.tagged_fields.into_iter().map(|tf| {
546 RawTaggedField::KnownSemantics(tf)
547 }).collect::<Vec<_>>();
549 let data = RawDataPart {
550 timestamp: timestamp,
551 tagged_fields: tagged_fields,
561 impl<H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<tb::False, H, T, C> {
562 /// Set the description. This function is only available if no description (hash) was set.
563 pub fn description(mut self, description: String) -> InvoiceBuilder<tb::True, H, T, C> {
564 match Description::new(description) {
565 Ok(d) => self.tagged_fields.push(TaggedField::Description(d)),
566 Err(e) => self.error = Some(e),
571 /// Set the description hash. This function is only available if no description (hash) was set.
572 pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder<tb::True, H, T, C> {
573 self.tagged_fields.push(TaggedField::DescriptionHash(Sha256(description_hash)));
578 impl<D: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, tb::False, T, C> {
579 /// Set the payment hash. This function is only available if no payment hash was set.
580 pub fn payment_hash(mut self, hash: sha256::Hash) -> InvoiceBuilder<D, tb::True, T, C> {
581 self.tagged_fields.push(TaggedField::PaymentHash(Sha256(hash)));
586 impl<D: tb::Bool, H: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, tb::False, C> {
587 /// Sets the timestamp.
588 pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder<D, H, tb::True, C> {
589 match PositiveTimestamp::from_system_time(time) {
590 Ok(t) => self.timestamp = Some(t),
591 Err(e) => self.error = Some(e),
597 /// Sets the timestamp to the current UNIX timestamp.
598 pub fn current_timestamp(mut self) -> InvoiceBuilder<D, H, tb::True, C> {
599 let now = PositiveTimestamp::from_system_time(SystemTime::now());
600 self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen"));
605 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T, tb::False> {
606 /// Sets `min_final_cltv_expiry`.
607 pub fn min_final_cltv_expiry(mut self, min_final_cltv_expiry: u64) -> InvoiceBuilder<D, H, T, tb::True> {
608 self.tagged_fields.push(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry(min_final_cltv_expiry)));
613 impl InvoiceBuilder<tb::True, tb::True, tb::True, tb::True> {
614 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
615 /// and MUST produce a recoverable signature valid for the given hash and if applicable also for
616 /// the included payee public key.
617 pub fn build_signed<F>(self, sign_function: F) -> Result<Invoice, CreationError>
618 where F: FnOnce(&Message) -> RecoverableSignature
620 let invoice = self.try_build_signed::<_, ()>(|hash| {
621 Ok(sign_function(hash))
626 Err(SignOrCreationError::CreationError(e)) => Err(e),
627 Err(SignOrCreationError::SignError(())) => unreachable!(),
631 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY fail with
632 /// an error of type `E` and MUST produce a recoverable signature valid for the given hash and
633 /// if applicable also for the included payee public key.
634 pub fn try_build_signed<F, E>(self, sign_function: F) -> Result<Invoice, SignOrCreationError<E>>
635 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
637 let raw = match self.build_raw() {
639 Err(e) => return Err(SignOrCreationError::CreationError(e)),
642 let signed = match raw.sign(sign_function) {
644 Err(e) => return Err(SignOrCreationError::SignError(e)),
647 let invoice = Invoice {
648 signed_invoice: signed,
651 invoice.check_field_counts().expect("should be ensured by type signature of builder");
658 impl SignedRawInvoice {
659 /// Disassembles the `SignedRawInvoice` into its three parts:
661 /// 2. hash of the raw invoice
663 pub fn into_parts(self) -> (RawInvoice, [u8; 32], InvoiceSignature) {
664 (self.raw_invoice, self.hash, self.signature)
667 /// The `RawInvoice` which was signed.
668 pub fn raw_invoice(&self) -> &RawInvoice {
672 /// The hash of the `RawInvoice` that was signed.
673 pub fn hash(&self) -> &[u8; 32] {
677 /// InvoiceSignature for the invoice.
678 pub fn signature(&self) -> &InvoiceSignature {
682 /// Recovers the public key used for signing the invoice from the recoverable signature.
683 pub fn recover_payee_pub_key(&self) -> Result<PayeePubKey, secp256k1::Error> {
684 let hash = Message::from_slice(&self.hash[..])
685 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
687 Ok(PayeePubKey(Secp256k1::new().recover(
693 /// Checks if the signature is valid for the included payee public key or if none exists if it's
694 /// valid for the recovered signature (which should always be true?).
695 pub fn check_signature(&self) -> bool {
696 let included_pub_key = self.raw_invoice.payee_pub_key();
698 let mut recovered_pub_key = Option::None;
699 if recovered_pub_key.is_none() {
700 let recovered = match self.recover_payee_pub_key() {
702 Err(_) => return false,
704 recovered_pub_key = Some(recovered);
707 let pub_key = included_pub_key.or_else(|| recovered_pub_key.as_ref())
708 .expect("One is always present");
710 let hash = Message::from_slice(&self.hash[..])
711 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
713 let secp_context = Secp256k1::new();
714 let verification_result = secp_context.verify(
716 &self.signature.to_standard(),
720 match verification_result {
727 /// Finds the first element of an enum stream of a given variant and extracts one member of the
728 /// variant. If no element was found `None` gets returned.
730 /// The following example would extract the first
739 /// let elements = vec![A(1), A(2), B(3), A(4)]
741 /// assert_eq!(find_extract!(elements.iter(), Enum::B(ref x), x), Some(3u16))
743 macro_rules! find_extract {
744 ($iter:expr, $enm:pat, $enm_var:ident) => {
745 $iter.filter_map(|tf| match *tf {
746 $enm => Some($enm_var),
752 #[allow(missing_docs)]
754 /// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
755 pub(crate) fn construct_invoice_preimage(hrp_bytes: &[u8], data_without_signature: &[u5]) -> Vec<u8> {
756 use bech32::FromBase32;
758 let mut preimage = Vec::<u8>::from(hrp_bytes);
760 let mut data_part = Vec::from(data_without_signature);
761 let overhang = (data_part.len() * 5) % 8;
763 // add padding if data does not end at a byte boundary
764 data_part.push(u5::try_from_u8(0).unwrap());
766 // if overhang is in (1..3) we need to add u5(0) padding two times
768 data_part.push(u5::try_from_u8(0).unwrap());
772 preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
773 .expect("No padding error may occur due to appended zero above."));
777 /// Hash the HRP as bytes and signatureless data part.
778 fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
779 let preimage = RawInvoice::construct_invoice_preimage(hrp_bytes, data_without_signature);
780 let mut hash: [u8; 32] = Default::default();
781 hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
785 /// Calculate the hash of the encoded `RawInvoice`
786 pub fn hash(&self) -> [u8; 32] {
787 use bech32::ToBase32;
789 RawInvoice::hash_from_parts(
790 self.hrp.to_string().as_bytes(),
791 &self.data.to_base32()
795 /// Signs the invoice using the supplied `sign_function`. This function MAY fail with an error
796 /// of type `E`. Since the signature of a `SignedRawInvoice` is not required to be valid there
797 /// are no constraints regarding the validity of the produced signature.
799 /// (C-not exported) As we don't currently support passing function pointers into methods
801 pub fn sign<F, E>(self, sign_method: F) -> Result<SignedRawInvoice, E>
802 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
804 let raw_hash = self.hash();
805 let hash = Message::from_slice(&raw_hash[..])
806 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
807 let signature = sign_method(&hash)?;
809 Ok(SignedRawInvoice {
812 signature: InvoiceSignature(signature),
816 /// Returns an iterator over all tagged fields with known semantics.
818 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
819 pub fn known_tagged_fields(&self)
820 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>>
822 // For 1.14.0 compatibility: closures' types can't be written an fn()->() in the
823 // function's type signature.
824 // TODO: refactor once impl Trait is available
825 fn match_raw(raw: &RawTaggedField) -> Option<&TaggedField> {
827 RawTaggedField::KnownSemantics(ref tf) => Some(tf),
832 self.data.tagged_fields.iter().filter_map(match_raw )
835 pub fn payment_hash(&self) -> Option<&Sha256> {
836 find_extract!(self.known_tagged_fields(), TaggedField::PaymentHash(ref x), x)
839 pub fn description(&self) -> Option<&Description> {
840 find_extract!(self.known_tagged_fields(), TaggedField::Description(ref x), x)
843 pub fn payee_pub_key(&self) -> Option<&PayeePubKey> {
844 find_extract!(self.known_tagged_fields(), TaggedField::PayeePubKey(ref x), x)
847 pub fn description_hash(&self) -> Option<&Sha256> {
848 find_extract!(self.known_tagged_fields(), TaggedField::DescriptionHash(ref x), x)
851 pub fn expiry_time(&self) -> Option<&ExpiryTime> {
852 find_extract!(self.known_tagged_fields(), TaggedField::ExpiryTime(ref x), x)
855 pub fn min_final_cltv_expiry(&self) -> Option<&MinFinalCltvExpiry> {
856 find_extract!(self.known_tagged_fields(), TaggedField::MinFinalCltvExpiry(ref x), x)
859 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
860 find_extract!(self.known_tagged_fields(), TaggedField::PaymentSecret(ref x), x)
863 pub fn features(&self) -> Option<&InvoiceFeatures> {
864 find_extract!(self.known_tagged_fields(), TaggedField::Features(ref x), x)
867 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
868 pub fn fallbacks(&self) -> Vec<&Fallback> {
869 self.known_tagged_fields().filter_map(|tf| match tf {
870 &TaggedField::Fallback(ref f) => Some(f),
872 }).collect::<Vec<&Fallback>>()
875 pub fn routes(&self) -> Vec<&RouteHint> {
876 self.known_tagged_fields().filter_map(|tf| match tf {
877 &TaggedField::Route(ref r) => Some(r),
879 }).collect::<Vec<&RouteHint>>()
882 pub fn amount_pico_btc(&self) -> Option<u64> {
883 self.hrp.raw_amount.map(|v| {
884 v * self.hrp.si_prefix.as_ref().map_or(1_000_000_000_000, |si| { si.multiplier() })
888 pub fn currency(&self) -> Currency {
889 self.hrp.currency.clone()
893 impl PositiveTimestamp {
894 /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
895 /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
896 /// `CreationError::TimestampOutOfBounds`.
897 pub fn from_unix_timestamp(unix_seconds: u64) -> Result<Self, CreationError> {
898 if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
899 Err(CreationError::TimestampOutOfBounds)
901 Ok(PositiveTimestamp(UNIX_EPOCH + Duration::from_secs(unix_seconds)))
905 /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
906 /// the Range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
907 /// `CreationError::TimestampOutOfBounds`.
908 pub fn from_system_time(time: SystemTime) -> Result<Self, CreationError> {
910 .duration_since(UNIX_EPOCH)
911 .map(|t| t.as_secs() <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)
914 Ok(PositiveTimestamp(time))
916 Err(CreationError::TimestampOutOfBounds)
920 /// Returns the UNIX timestamp representing the stored time
921 pub fn as_unix_timestamp(&self) -> u64 {
922 self.0.duration_since(UNIX_EPOCH)
923 .expect("ensured by type contract/constructors")
927 /// Returns a reference to the internal `SystemTime` time representation
928 pub fn as_time(&self) -> &SystemTime {
933 impl Into<SystemTime> for PositiveTimestamp {
934 fn into(self) -> SystemTime {
939 impl Deref for PositiveTimestamp {
940 type Target = SystemTime;
942 fn deref(&self) -> &Self::Target {
948 /// Transform the `Invoice` into it's unchecked version
949 pub fn into_signed_raw(self) -> SignedRawInvoice {
953 /// Check that all mandatory fields are present
954 fn check_field_counts(&self) -> Result<(), SemanticError> {
955 // "A writer MUST include exactly one p field […]."
956 let payment_hash_cnt = self.tagged_fields().filter(|&tf| match *tf {
957 TaggedField::PaymentHash(_) => true,
960 if payment_hash_cnt < 1 {
961 return Err(SemanticError::NoPaymentHash);
962 } else if payment_hash_cnt > 1 {
963 return Err(SemanticError::MultiplePaymentHashes);
966 // "A writer MUST include either exactly one d or exactly one h field."
967 let description_cnt = self.tagged_fields().filter(|&tf| match *tf {
968 TaggedField::Description(_) | TaggedField::DescriptionHash(_) => true,
971 if description_cnt < 1 {
972 return Err(SemanticError::NoDescription);
973 } else if description_cnt > 1 {
974 return Err(SemanticError::MultipleDescriptions);
980 /// Check that the invoice is signed correctly and that key recovery works
981 pub fn check_signature(&self) -> Result<(), SemanticError> {
982 match self.signed_invoice.recover_payee_pub_key() {
983 Err(secp256k1::Error::InvalidRecoveryId) =>
984 return Err(SemanticError::InvalidRecoveryId),
985 Err(_) => panic!("no other error may occur"),
989 if !self.signed_invoice.check_signature() {
990 return Err(SemanticError::InvalidSignature);
996 /// Constructs an `Invoice` from a `SignedInvoice` by checking all its invariants.
998 /// use lightning_invoice::*;
1000 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
1001 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
1002 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
1003 /// ky03ylcqca784w";
1005 /// let signed = invoice.parse::<SignedRawInvoice>().unwrap();
1007 /// assert!(Invoice::from_signed(signed).is_ok());
1009 pub fn from_signed(signed_invoice: SignedRawInvoice) -> Result<Self, SemanticError> {
1010 let invoice = Invoice {
1011 signed_invoice: signed_invoice,
1013 invoice.check_field_counts()?;
1014 invoice.check_signature()?;
1019 /// Returns the `Invoice`'s timestamp (should equal it's creation time)
1020 pub fn timestamp(&self) -> &SystemTime {
1021 self.signed_invoice.raw_invoice().data.timestamp.as_time()
1024 /// Returns an iterator over all tagged fields of this Invoice.
1026 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
1027 pub fn tagged_fields(&self)
1028 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>> {
1029 self.signed_invoice.raw_invoice().known_tagged_fields()
1032 /// Returns the hash to which we will receive the preimage on completion of the payment
1033 pub fn payment_hash(&self) -> &sha256::Hash {
1034 &self.signed_invoice.payment_hash().expect("checked by constructor").0
1037 /// Return the description or a hash of it for longer ones
1039 /// (C-not exported) because we don't yet export InvoiceDescription
1040 pub fn description(&self) -> InvoiceDescription {
1041 if let Some(ref direct) = self.signed_invoice.description() {
1042 return InvoiceDescription::Direct(direct);
1043 } else if let Some(ref hash) = self.signed_invoice.description_hash() {
1044 return InvoiceDescription::Hash(hash);
1046 unreachable!("ensured by constructor");
1049 /// Get the payee's public key if one was included in the invoice
1050 pub fn payee_pub_key(&self) -> Option<&PublicKey> {
1051 self.signed_invoice.payee_pub_key().map(|x| &x.0)
1054 /// Get the payment secret if one was included in the invoice
1055 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
1056 self.signed_invoice.payment_secret()
1059 /// Get the invoice features if they were included in the invoice
1060 pub fn features(&self) -> Option<&InvoiceFeatures> {
1061 self.signed_invoice.features()
1064 /// Recover the payee's public key (only to be used if none was included in the invoice)
1065 pub fn recover_payee_pub_key(&self) -> PublicKey {
1066 self.signed_invoice.recover_payee_pub_key().expect("was checked by constructor").0
1069 /// Returns the invoice's expiry time, if present, otherwise [`DEFAULT_EXPIRY_TIME`].
1070 pub fn expiry_time(&self) -> Duration {
1071 self.signed_invoice.expiry_time()
1073 .unwrap_or(Duration::from_secs(DEFAULT_EXPIRY_TIME))
1076 /// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
1077 /// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
1078 pub fn min_final_cltv_expiry(&self) -> u64 {
1079 self.signed_invoice.min_final_cltv_expiry()
1081 .unwrap_or(DEFAULT_MIN_FINAL_CLTV_EXPIRY)
1084 /// Returns a list of all fallback addresses
1086 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
1087 pub fn fallbacks(&self) -> Vec<&Fallback> {
1088 self.signed_invoice.fallbacks()
1091 /// Returns a list of all routes included in the invoice
1092 pub fn routes(&self) -> Vec<&RouteHint> {
1093 self.signed_invoice.routes()
1096 /// Returns the currency for which the invoice was issued
1097 pub fn currency(&self) -> Currency {
1098 self.signed_invoice.currency()
1101 /// Returns the amount if specified in the invoice as pico <currency>.
1102 pub fn amount_pico_btc(&self) -> Option<u64> {
1103 self.signed_invoice.amount_pico_btc()
1107 impl From<TaggedField> for RawTaggedField {
1108 fn from(tf: TaggedField) -> Self {
1109 RawTaggedField::KnownSemantics(tf)
1114 /// Numeric representation of the field's tag
1115 pub fn tag(&self) -> u5 {
1116 let tag = match *self {
1117 TaggedField::PaymentHash(_) => constants::TAG_PAYMENT_HASH,
1118 TaggedField::Description(_) => constants::TAG_DESCRIPTION,
1119 TaggedField::PayeePubKey(_) => constants::TAG_PAYEE_PUB_KEY,
1120 TaggedField::DescriptionHash(_) => constants::TAG_DESCRIPTION_HASH,
1121 TaggedField::ExpiryTime(_) => constants::TAG_EXPIRY_TIME,
1122 TaggedField::MinFinalCltvExpiry(_) => constants::TAG_MIN_FINAL_CLTV_EXPIRY,
1123 TaggedField::Fallback(_) => constants::TAG_FALLBACK,
1124 TaggedField::Route(_) => constants::TAG_ROUTE,
1125 TaggedField::PaymentSecret(_) => constants::TAG_PAYMENT_SECRET,
1126 TaggedField::Features(_) => constants::TAG_FEATURES,
1129 u5::try_from_u8(tag).expect("all tags defined are <32")
1135 /// Creates a new `Description` if `description` is at most 1023 __bytes__ long,
1136 /// returns `CreationError::DescriptionTooLong` otherwise
1138 /// Please note that single characters may use more than one byte due to UTF8 encoding.
1139 pub fn new(description: String) -> Result<Description, CreationError> {
1140 if description.len() > 639 {
1141 Err(CreationError::DescriptionTooLong)
1143 Ok(Description(description))
1147 /// Returns the underlying description `String`
1148 pub fn into_inner(self) -> String {
1153 impl Into<String> for Description {
1154 fn into(self) -> String {
1159 impl Deref for Description {
1162 fn deref(&self) -> &str {
1167 impl From<PublicKey> for PayeePubKey {
1168 fn from(pk: PublicKey) -> Self {
1173 impl Deref for PayeePubKey {
1174 type Target = PublicKey;
1176 fn deref(&self) -> &PublicKey {
1182 /// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1183 /// overflow on adding the `EpiryTime` to it then this function will return a
1184 /// `CreationError::ExpiryTimeOutOfBounds`.
1185 pub fn from_seconds(seconds: u64) -> Result<ExpiryTime, CreationError> {
1186 if seconds <= MAX_EXPIRY_TIME {
1187 Ok(ExpiryTime(Duration::from_secs(seconds)))
1189 Err(CreationError::ExpiryTimeOutOfBounds)
1193 /// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1194 /// would overflow on adding the `EpiryTime` to it then this function will return a
1195 /// `CreationError::ExpiryTimeOutOfBounds`.
1196 pub fn from_duration(duration: Duration) -> Result<ExpiryTime, CreationError> {
1197 if duration.as_secs() <= MAX_EXPIRY_TIME {
1198 Ok(ExpiryTime(duration))
1200 Err(CreationError::ExpiryTimeOutOfBounds)
1204 /// Returns the expiry time in seconds
1205 pub fn as_seconds(&self) -> u64 {
1209 /// Returns a reference to the underlying `Duration` (=expiry time)
1210 pub fn as_duration(&self) -> &Duration {
1216 /// Create a new (partial) route from a list of hops
1217 pub fn new(hops: Vec<RouteHintHop>) -> Result<RouteHint, CreationError> {
1218 if hops.len() <= 12 {
1221 Err(CreationError::RouteTooLong)
1225 /// Returrn the underlying vector of hops
1226 pub fn into_inner(self) -> Vec<RouteHintHop> {
1231 impl Into<Vec<RouteHintHop>> for RouteHint {
1232 fn into(self) -> Vec<RouteHintHop> {
1237 impl Deref for RouteHint {
1238 type Target = Vec<RouteHintHop>;
1240 fn deref(&self) -> &Vec<RouteHintHop> {
1245 impl Deref for InvoiceSignature {
1246 type Target = RecoverableSignature;
1248 fn deref(&self) -> &RecoverableSignature {
1253 impl Deref for SignedRawInvoice {
1254 type Target = RawInvoice;
1256 fn deref(&self) -> &RawInvoice {
1261 /// Errors that may occur when constructing a new `RawInvoice` or `Invoice`
1262 #[derive(Eq, PartialEq, Debug, Clone)]
1263 pub enum CreationError {
1264 /// The supplied description string was longer than 639 __bytes__ (see [`Description::new(…)`](./struct.Description.html#method.new))
1267 /// The specified route has too many hops and can't be encoded
1270 /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1271 TimestampOutOfBounds,
1273 /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1274 ExpiryTimeOutOfBounds,
1277 impl Display for CreationError {
1278 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1280 CreationError::DescriptionTooLong => f.write_str("The supplied description string was longer than 639 bytes"),
1281 CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
1282 CreationError::TimestampOutOfBounds => f.write_str("The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`"),
1283 CreationError::ExpiryTimeOutOfBounds => f.write_str("The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`"),
1288 impl std::error::Error for CreationError { }
1290 /// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
1291 /// requirements sections in BOLT #11
1292 #[derive(Eq, PartialEq, Debug, Clone)]
1293 pub enum SemanticError {
1294 /// The invoice is missing the mandatory payment hash
1297 /// The invoice has multiple payment hashes which isn't allowed
1298 MultiplePaymentHashes,
1300 /// No description or description hash are part of the invoice
1303 /// The invoice contains multiple descriptions and/or description hashes which isn't allowed
1304 MultipleDescriptions,
1306 /// The recovery id doesn't fit the signature/pub key
1309 /// The invoice's signature is invalid
1313 impl Display for SemanticError {
1314 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1316 SemanticError::NoPaymentHash => f.write_str("The invoice is missing the mandatory payment hash"),
1317 SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
1318 SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
1319 SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
1320 SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
1321 SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
1326 impl std::error::Error for SemanticError { }
1328 /// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
1330 #[derive(Eq, PartialEq, Debug, Clone)]
1331 pub enum SignOrCreationError<S = ()> {
1332 /// An error occurred during signing
1335 /// An error occurred while building the transaction
1336 CreationError(CreationError),
1339 impl<S> Display for SignOrCreationError<S> {
1340 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1342 SignOrCreationError::SignError(_) => f.write_str("An error occurred during signing"),
1343 SignOrCreationError::CreationError(err) => err.fmt(f),
1350 use bitcoin_hashes::hex::FromHex;
1351 use bitcoin_hashes::sha256;
1354 fn test_system_time_bounds_assumptions() {
1358 ::PositiveTimestamp::from_unix_timestamp(::SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1),
1359 Err(::CreationError::TimestampOutOfBounds)
1363 ::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
1364 Err(::CreationError::ExpiryTimeOutOfBounds)
1369 fn test_calc_invoice_hash() {
1370 use ::{RawInvoice, RawHrp, RawDataPart, Currency, PositiveTimestamp};
1371 use ::TaggedField::*;
1373 let invoice = RawInvoice {
1375 currency: Currency::Bitcoin,
1380 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1381 tagged_fields: vec![
1382 PaymentHash(::Sha256(sha256::Hash::from_hex(
1383 "0001020304050607080900010203040506070809000102030405060708090102"
1384 ).unwrap())).into(),
1385 Description(::Description::new(
1386 "Please consider supporting this project".to_owned()
1392 let expected_hash = [
1393 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27, 0x7b, 0x1d,
1394 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7, 0x83, 0x5d, 0xb2, 0xec,
1395 0xd5, 0x18, 0xe1, 0xc9
1398 assert_eq!(invoice.hash(), expected_hash)
1402 fn test_check_signature() {
1404 use secp256k1::Secp256k1;
1405 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1406 use secp256k1::key::{SecretKey, PublicKey};
1407 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1410 let invoice = SignedRawInvoice {
1411 raw_invoice: RawInvoice {
1413 currency: Currency::Bitcoin,
1418 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1419 tagged_fields: vec ! [
1420 PaymentHash(Sha256(sha256::Hash::from_hex(
1421 "0001020304050607080900010203040506070809000102030405060708090102"
1422 ).unwrap())).into(),
1425 "Please consider supporting this project".to_owned()
1432 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1433 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1434 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1436 signature: InvoiceSignature(RecoverableSignature::from_compact(
1438 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1439 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1440 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1441 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1442 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1443 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1445 RecoveryId::from_i32(0).unwrap()
1449 assert!(invoice.check_signature());
1451 let private_key = SecretKey::from_slice(
1453 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1454 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1455 0x3b, 0x2d, 0xb7, 0x34
1458 let public_key = PublicKey::from_secret_key(&Secp256k1::new(), &private_key);
1460 assert_eq!(invoice.recover_payee_pub_key(), Ok(::PayeePubKey(public_key)));
1462 let (raw_invoice, _, _) = invoice.into_parts();
1463 let new_signed = raw_invoice.sign::<_, ()>(|hash| {
1464 Ok(Secp256k1::new().sign_recoverable(hash, &private_key))
1467 assert!(new_signed.check_signature());
1471 fn test_builder_amount() {
1474 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1475 .description("Test".into())
1476 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1477 .current_timestamp();
1479 let invoice = builder.clone()
1480 .amount_pico_btc(15000)
1484 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Nano));
1485 assert_eq!(invoice.hrp.raw_amount, Some(15));
1488 let invoice = builder.clone()
1489 .amount_pico_btc(1500)
1493 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Pico));
1494 assert_eq!(invoice.hrp.raw_amount, Some(1500));
1498 fn test_builder_fail() {
1500 use std::iter::FromIterator;
1501 use secp256k1::key::PublicKey;
1503 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1504 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1505 .current_timestamp()
1506 .min_final_cltv_expiry(144);
1508 let too_long_string = String::from_iter(
1509 (0..1024).map(|_| '?')
1512 let long_desc_res = builder.clone()
1513 .description(too_long_string)
1515 assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
1517 let route_hop = RouteHintHop {
1518 src_node_id: PublicKey::from_slice(
1520 0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
1521 0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
1522 0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
1525 short_channel_id: 0,
1528 proportional_millionths: 0,
1530 cltv_expiry_delta: 0,
1531 htlc_minimum_msat: None,
1532 htlc_maximum_msat: None,
1534 let too_long_route = vec![route_hop; 13];
1535 let long_route_res = builder.clone()
1536 .description("Test".into())
1537 .route(too_long_route)
1539 assert_eq!(long_route_res, Err(CreationError::RouteTooLong));
1541 let sign_error_res = builder.clone()
1542 .description("Test".into())
1543 .try_build_signed(|_| {
1544 Err("ImaginaryError")
1546 assert_eq!(sign_error_res, Err(SignOrCreationError::SignError("ImaginaryError")));
1550 fn test_builder_ok() {
1552 use secp256k1::Secp256k1;
1553 use secp256k1::key::{SecretKey, PublicKey};
1554 use std::time::{UNIX_EPOCH, Duration};
1556 let secp_ctx = Secp256k1::new();
1558 let private_key = SecretKey::from_slice(
1560 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1561 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1562 0x3b, 0x2d, 0xb7, 0x34
1565 let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
1569 src_node_id: public_key.clone(),
1570 short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
1573 proportional_millionths: 1,
1575 cltv_expiry_delta: 145,
1576 htlc_minimum_msat: None,
1577 htlc_maximum_msat: None,
1580 src_node_id: public_key.clone(),
1581 short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
1584 proportional_millionths: 2,
1586 cltv_expiry_delta: 146,
1587 htlc_minimum_msat: None,
1588 htlc_maximum_msat: None,
1594 src_node_id: public_key.clone(),
1595 short_channel_id: 0,
1598 proportional_millionths: 3,
1600 cltv_expiry_delta: 147,
1601 htlc_minimum_msat: None,
1602 htlc_maximum_msat: None,
1605 src_node_id: public_key.clone(),
1606 short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
1609 proportional_millionths: 4,
1611 cltv_expiry_delta: 148,
1612 htlc_minimum_msat: None,
1613 htlc_maximum_msat: None,
1617 let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
1618 .amount_pico_btc(123)
1619 .timestamp(UNIX_EPOCH + Duration::from_secs(1234567))
1620 .payee_pub_key(public_key.clone())
1621 .expiry_time(Duration::from_secs(54321))
1622 .min_final_cltv_expiry(144)
1623 .fallback(Fallback::PubKeyHash([0;20]))
1624 .route(route_1.clone())
1625 .route(route_2.clone())
1626 .description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
1627 .payment_hash(sha256::Hash::from_slice(&[21;32][..]).unwrap());
1629 let invoice = builder.clone().build_signed(|hash| {
1630 secp_ctx.sign_recoverable(hash, &private_key)
1633 assert!(invoice.check_signature().is_ok());
1634 assert_eq!(invoice.tagged_fields().count(), 8);
1636 assert_eq!(invoice.amount_pico_btc(), Some(123));
1637 assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
1639 invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),
1642 assert_eq!(invoice.payee_pub_key(), Some(&public_key));
1643 assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
1644 assert_eq!(invoice.min_final_cltv_expiry(), 144);
1645 assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
1646 assert_eq!(invoice.routes(), vec![&RouteHint(route_1), &RouteHint(route_2)]);
1648 invoice.description(),
1649 InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
1651 assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&[21;32][..]).unwrap());
1653 let raw_invoice = builder.build_raw().unwrap();
1654 assert_eq!(raw_invoice, *invoice.into_signed_raw().raw_invoice())
1658 fn test_default_values() {
1660 use secp256k1::Secp256k1;
1661 use secp256k1::key::SecretKey;
1663 let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
1664 .description("Test".into())
1665 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1666 .current_timestamp()
1669 .sign::<_, ()>(|hash| {
1670 let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
1671 let secp_ctx = Secp256k1::new();
1672 Ok(secp_ctx.sign_recoverable(hash, &privkey))
1675 let invoice = Invoice::from_signed(signed_invoice).unwrap();
1677 assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
1678 assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));