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))]
9 #![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
11 //! This crate provides data structures to represent
12 //! [lightning BOLT11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md)
13 //! invoices and functions to create, encode and decode these. If you just want to use the standard
14 //! en-/decoding functionality this should get you started:
16 //! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
17 //! * For constructing invoices use the `InvoiceBuilder`
18 //! * For serializing invoices use the `Display`/`ToString` traits
20 #[cfg(not(any(feature = "std", feature = "no-std")))]
21 compile_error!("at least one of the `std` or `no-std` features must be enabled");
27 extern crate bitcoin_hashes;
28 #[macro_use] extern crate lightning;
29 extern crate num_traits;
30 extern crate secp256k1;
32 #[cfg(any(test, feature = "std"))]
35 #[cfg(feature = "std")]
36 use std::time::SystemTime;
39 use bitcoin_hashes::Hash;
40 use bitcoin_hashes::sha256;
41 use lightning::ln::PaymentSecret;
42 use lightning::ln::features::InvoiceFeatures;
43 #[cfg(any(doc, test))]
44 use lightning::routing::network_graph::RoutingFees;
45 use lightning::routing::router::RouteHint;
47 use secp256k1::key::PublicKey;
48 use secp256k1::{Message, Secp256k1};
49 use secp256k1::recovery::RecoverableSignature;
51 use core::fmt::{Display, Formatter, self};
52 use core::iter::FilterMap;
54 use core::slice::Iter;
55 use core::time::Duration;
62 #[cfg(feature = "hashbrown")]
63 extern crate hashbrown;
65 pub use alloc::{vec, vec::Vec, string::String, collections::VecDeque, boxed::Box};
66 #[cfg(not(feature = "hashbrown"))]
67 pub use std::collections::{HashMap, HashSet, hash_map};
68 #[cfg(feature = "hashbrown")]
69 pub use self::hashbrown::{HashMap, HashSet, hash_map};
71 pub use alloc::string::ToString;
76 /// Sync compat for std/no_std
77 #[cfg(feature = "std")]
79 pub use ::std::sync::{Mutex, MutexGuard};
82 /// Sync compat for std/no_std
83 #[cfg(not(feature = "std"))]
86 pub use de::{ParseError, ParseOrSemanticError};
88 // TODO: fix before 2037 (see rust PR #55527)
89 /// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
90 /// one of the unit tests, please run them.
91 const SYSTEM_TIME_MAX_UNIX_TIMESTAMP: u64 = core::i32::MAX as u64;
93 /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
94 /// it should be rather low as long as we still have to support 32bit time representations
95 const MAX_EXPIRY_TIME: u64 = 60 * 60 * 24 * 356;
97 /// Default expiry time as defined by [BOLT 11].
99 /// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
100 pub const DEFAULT_EXPIRY_TIME: u64 = 3600;
102 /// Default minimum final CLTV expiry as defined by [BOLT 11].
104 /// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry, which is
105 /// provided in [`MIN_FINAL_CLTV_EXPIRY`].
107 /// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
108 /// [`MIN_FINAL_CLTV_EXPIRY`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY
109 pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY: u64 = 18;
111 /// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
112 /// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
113 /// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
114 /// please open an issue. If all tests pass you should be able to use this library safely by just
115 /// removing this function till we patch it accordingly.
116 #[cfg(feature = "std")]
117 fn __system_time_size_check() {
118 // Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
119 // a `Duration` since `SystemTime::UNIX_EPOCH`.
120 unsafe { core::mem::transmute_copy::<SystemTime, [u8; 16]>(&SystemTime::UNIX_EPOCH); }
124 /// **Call this function on startup to ensure that all assumptions about the platform are valid.**
126 /// Unfortunately we have to make assumptions about the upper bounds of the `SystemTime` type on
127 /// your platform which we can't fully verify at compile time and which isn't part of it's contract.
128 /// To our best knowledge our assumptions hold for all platforms officially supported by rust, but
129 /// since this check is fast we recommend to do it anyway.
131 /// If this function fails this is considered a bug. Please open an issue describing your
132 /// platform and stating your current system time.
134 /// Note that this currently does nothing in `no_std` environments, because they don't have
135 /// a `SystemTime` implementation.
138 /// If the check fails this function panics. By calling this function on startup you ensure that
139 /// this wont happen at an arbitrary later point in time.
140 pub fn check_platform() {
141 #[cfg(feature = "std")]
142 check_system_time_bounds();
145 #[cfg(feature = "std")]
146 fn check_system_time_bounds() {
147 // The upper and lower bounds of `SystemTime` are not part of its public contract and are
148 // platform specific. That's why we have to test if our assumptions regarding these bounds
149 // hold on the target platform.
151 // If this test fails on your platform, please don't use the library and open an issue
152 // instead so we can resolve the situation. Currently this library is tested on:
154 let fail_date = SystemTime::UNIX_EPOCH + Duration::from_secs(SYSTEM_TIME_MAX_UNIX_TIMESTAMP);
155 let year = Duration::from_secs(60 * 60 * 24 * 365);
157 // Make sure that the library will keep working for another year
158 assert!(fail_date.duration_since(SystemTime::now()).unwrap() > year);
160 let max_ts = PositiveTimestamp::from_unix_timestamp(
161 SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
163 let max_exp = ::ExpiryTime::from_seconds(MAX_EXPIRY_TIME).unwrap();
166 (max_ts.as_time() + *max_exp.as_duration()).duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(),
167 SYSTEM_TIME_MAX_UNIX_TIMESTAMP
172 /// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
173 /// that only a semantically and syntactically correct Invoice can be built using it.
176 /// extern crate secp256k1;
177 /// extern crate lightning;
178 /// extern crate lightning_invoice;
179 /// extern crate bitcoin_hashes;
181 /// use bitcoin_hashes::Hash;
182 /// use bitcoin_hashes::sha256;
184 /// use secp256k1::Secp256k1;
185 /// use secp256k1::key::SecretKey;
187 /// use lightning::ln::PaymentSecret;
189 /// use lightning_invoice::{Currency, InvoiceBuilder};
191 /// # #[cfg(not(feature = "std"))]
193 /// # #[cfg(feature = "std")]
195 /// let private_key = SecretKey::from_slice(
197 /// 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f,
198 /// 0xe2, 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04,
199 /// 0xa8, 0xca, 0x3b, 0x2d, 0xb7, 0x34
203 /// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
204 /// let payment_secret = PaymentSecret([42u8; 32]);
206 /// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
207 /// .description("Coins pls!".into())
208 /// .payment_hash(payment_hash)
209 /// .payment_secret(payment_secret)
210 /// .current_timestamp()
211 /// .min_final_cltv_expiry(144)
212 /// .build_signed(|hash| {
213 /// Secp256k1::new().sign_recoverable(hash, &private_key)
217 /// assert!(invoice.to_string().starts_with("lnbc1"));
221 /// # Type parameters
222 /// The two parameters `D` and `H` signal if the builder already contains the correct amount of the
224 /// * `D`: exactly one `Description` or `DescriptionHash`
225 /// * `H`: exactly one `PaymentHash`
226 /// * `T`: the timestamp is set
228 /// (C-not exported) as we likely need to manually select one set of boolean type parameters.
229 #[derive(Eq, PartialEq, Debug, Clone)]
230 pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> {
233 si_prefix: Option<SiPrefix>,
234 timestamp: Option<PositiveTimestamp>,
235 tagged_fields: Vec<TaggedField>,
236 error: Option<CreationError>,
238 phantom_d: core::marker::PhantomData<D>,
239 phantom_h: core::marker::PhantomData<H>,
240 phantom_t: core::marker::PhantomData<T>,
241 phantom_c: core::marker::PhantomData<C>,
242 phantom_s: core::marker::PhantomData<S>,
245 /// Represents a syntactically and semantically correct lightning BOLT11 invoice.
247 /// There are three ways to construct an `Invoice`:
248 /// 1. using `InvoiceBuilder`
249 /// 2. using `Invoice::from_signed(SignedRawInvoice)`
250 /// 3. using `str::parse::<Invoice>(&str)`
251 #[derive(Eq, PartialEq, Debug, Clone)]
253 signed_invoice: SignedRawInvoice,
256 /// Represents the description of an invoice which has to be either a directly included string or
257 /// a hash of a description provided out of band.
259 /// (C-not exported) As we don't have a good way to map the reference lifetimes making this
260 /// practically impossible to use safely in languages like C.
261 #[derive(Eq, PartialEq, Debug, Clone)]
262 pub enum InvoiceDescription<'f> {
263 /// Reference to the directly supplied description in the invoice
264 Direct(&'f Description),
266 /// Reference to the description's hash included in the invoice
270 /// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be
274 /// The hash has to be either from the deserialized invoice or from the serialized `raw_invoice`.
275 #[derive(Eq, PartialEq, Debug, Clone)]
276 pub struct SignedRawInvoice {
277 /// The rawInvoice that the signature belongs to
278 raw_invoice: RawInvoice,
280 /// Hash of the `RawInvoice` that will be used to check the signature.
282 /// * if the `SignedRawInvoice` was deserialized the hash is of from the original encoded form,
283 /// since it's not guaranteed that encoding it again will lead to the same result since integers
284 /// could have been encoded with leading zeroes etc.
285 /// * if the `SignedRawInvoice` was constructed manually the hash will be the calculated hash
286 /// from the `RawInvoice`
289 /// signature of the payment request
290 signature: InvoiceSignature,
293 /// Represents an syntactically correct Invoice for a payment on the lightning network,
294 /// but without the signature information.
295 /// De- and encoding should not lead to information loss but may lead to different hashes.
297 /// For methods without docs see the corresponding methods in `Invoice`.
298 #[derive(Eq, PartialEq, Debug, Clone)]
299 pub struct RawInvoice {
300 /// human readable part
304 pub data: RawDataPart,
307 /// Data of the `RawInvoice` that is encoded in the human readable part
309 /// (C-not exported) As we don't yet support Option<Enum>
310 #[derive(Eq, PartialEq, Debug, Clone)]
312 /// The currency deferred from the 3rd and 4th character of the bech32 transaction
313 pub currency: Currency,
315 /// The amount that, multiplied by the SI prefix, has to be payed
316 pub raw_amount: Option<u64>,
318 /// SI prefix that gets multiplied with the `raw_amount`
319 pub si_prefix: Option<SiPrefix>,
322 /// Data of the `RawInvoice` that is encoded in the data part
323 #[derive(Eq, PartialEq, Debug, Clone)]
324 pub struct RawDataPart {
325 /// generation time of the invoice
326 pub timestamp: PositiveTimestamp,
328 /// tagged fields of the payment request
329 pub tagged_fields: Vec<RawTaggedField>,
332 /// A timestamp that refers to a date after 1 January 1970 which means its representation as UNIX
333 /// timestamp is positive.
336 /// The UNIX timestamp representing the stored time has to be positive and small enough so that
337 /// a `ExpiryTime` can be added to it without an overflow.
338 #[derive(Eq, PartialEq, Debug, Clone)]
339 pub struct PositiveTimestamp(Duration);
341 /// SI prefixes for the human readable part
342 #[derive(Eq, PartialEq, Debug, Clone, Copy)]
355 /// Returns the multiplier to go from a BTC value to picoBTC implied by this SiPrefix.
356 /// This is effectively 10^12 * the prefix multiplier
357 pub fn multiplier(&self) -> u64 {
359 SiPrefix::Milli => 1_000_000_000,
360 SiPrefix::Micro => 1_000_000,
361 SiPrefix::Nano => 1_000,
366 /// Returns all enum variants of `SiPrefix` sorted in descending order of their associated
369 /// (C-not exported) As we don't yet support a slice of enums, and also because this function
370 /// isn't the most critical to expose.
371 pub fn values_desc() -> &'static [SiPrefix] {
373 static VALUES: [SiPrefix; 4] = [Milli, Micro, Nano, Pico];
378 /// Enum representing the crypto currencies (or networks) supported by this library
379 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
397 /// Tagged field which may have an unknown tag
399 /// (C-not exported) as we don't currently support TaggedField
400 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
401 pub enum RawTaggedField {
402 /// Parsed tagged field with known tag
403 KnownSemantics(TaggedField),
404 /// tagged field which was not parsed due to an unknown tag or undefined field semantics
405 UnknownSemantics(Vec<u5>),
408 /// Tagged field with known tag
410 /// For descriptions of the enum values please refer to the enclosed type's docs.
412 /// (C-not exported) As we don't yet support enum variants with the same name the struct contained
414 #[allow(missing_docs)]
415 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
416 pub enum TaggedField {
418 Description(Description),
419 PayeePubKey(PayeePubKey),
420 DescriptionHash(Sha256),
421 ExpiryTime(ExpiryTime),
422 MinFinalCltvExpiry(MinFinalCltvExpiry),
424 PrivateRoute(PrivateRoute),
425 PaymentSecret(PaymentSecret),
426 Features(InvoiceFeatures),
430 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
431 pub struct Sha256(/// (C-not exported) as the native hash types are not currently mapped
434 /// Description string
437 /// The description can be at most 639 __bytes__ long
438 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
439 pub struct Description(String);
442 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
443 pub struct PayeePubKey(pub PublicKey);
445 /// Positive duration that defines when (relatively to the timestamp) in the future the invoice
449 /// The number of seconds this expiry time represents has to be in the range
450 /// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
452 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
453 pub struct ExpiryTime(Duration);
455 /// `min_final_cltv_expiry` to use for the last HTLC in the route
456 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
457 pub struct MinFinalCltvExpiry(pub u64);
459 // TODO: better types instead onf byte arrays
460 /// Fallback address in case no LN payment is possible
461 #[allow(missing_docs)]
462 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
468 PubKeyHash([u8; 20]),
469 ScriptHash([u8; 20]),
472 /// Recoverable signature
473 #[derive(Clone, Debug, Eq, PartialEq)]
474 pub struct InvoiceSignature(pub RecoverableSignature);
476 /// Private routing information
479 /// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
481 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
482 pub struct PrivateRoute(RouteHint);
484 /// Tag constants as specified in BOLT11
485 #[allow(missing_docs)]
487 pub const TAG_PAYMENT_HASH: u8 = 1;
488 pub const TAG_DESCRIPTION: u8 = 13;
489 pub const TAG_PAYEE_PUB_KEY: u8 = 19;
490 pub const TAG_DESCRIPTION_HASH: u8 = 23;
491 pub const TAG_EXPIRY_TIME: u8 = 6;
492 pub const TAG_MIN_FINAL_CLTV_EXPIRY: u8 = 24;
493 pub const TAG_FALLBACK: u8 = 9;
494 pub const TAG_PRIVATE_ROUTE: u8 = 3;
495 pub const TAG_PAYMENT_SECRET: u8 = 16;
496 pub const TAG_FEATURES: u8 = 5;
499 impl InvoiceBuilder<tb::False, tb::False, tb::False, tb::False, tb::False> {
500 /// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
501 /// `InvoiceBuilder::build(self)` becomes available.
502 pub fn new(currrency: Currency) -> Self {
508 tagged_fields: Vec::new(),
511 phantom_d: core::marker::PhantomData,
512 phantom_h: core::marker::PhantomData,
513 phantom_t: core::marker::PhantomData,
514 phantom_c: core::marker::PhantomData,
515 phantom_s: core::marker::PhantomData,
520 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, T, C, S> {
521 /// Helper function to set the completeness flags.
522 fn set_flags<DN: tb::Bool, HN: tb::Bool, TN: tb::Bool, CN: tb::Bool, SN: tb::Bool>(self) -> InvoiceBuilder<DN, HN, TN, CN, SN> {
523 InvoiceBuilder::<DN, HN, TN, CN, SN> {
524 currency: self.currency,
526 si_prefix: self.si_prefix,
527 timestamp: self.timestamp,
528 tagged_fields: self.tagged_fields,
531 phantom_d: core::marker::PhantomData,
532 phantom_h: core::marker::PhantomData,
533 phantom_t: core::marker::PhantomData,
534 phantom_c: core::marker::PhantomData,
535 phantom_s: core::marker::PhantomData,
539 /// Sets the amount in millisatoshis. The optimal SI prefix is chosen automatically.
540 pub fn amount_milli_satoshis(mut self, amount_msat: u64) -> Self {
541 let amount = amount_msat * 10; // Invoices are denominated in "pico BTC"
542 let biggest_possible_si_prefix = SiPrefix::values_desc()
544 .find(|prefix| amount % prefix.multiplier() == 0)
545 .expect("Pico should always match");
546 self.amount = Some(amount / biggest_possible_si_prefix.multiplier());
547 self.si_prefix = Some(*biggest_possible_si_prefix);
551 /// Sets the payee's public key.
552 pub fn payee_pub_key(mut self, pub_key: PublicKey) -> Self {
553 self.tagged_fields.push(TaggedField::PayeePubKey(PayeePubKey(pub_key)));
557 /// Sets the expiry time
558 pub fn expiry_time(mut self, expiry_time: Duration) -> Self {
559 match ExpiryTime::from_duration(expiry_time) {
560 Ok(t) => self.tagged_fields.push(TaggedField::ExpiryTime(t)),
561 Err(e) => self.error = Some(e),
566 /// Adds a fallback address.
567 pub fn fallback(mut self, fallback: Fallback) -> Self {
568 self.tagged_fields.push(TaggedField::Fallback(fallback));
572 /// Adds a private route.
573 pub fn private_route(mut self, hint: RouteHint) -> Self {
574 match PrivateRoute::new(hint) {
575 Ok(r) => self.tagged_fields.push(TaggedField::PrivateRoute(r)),
576 Err(e) => self.error = Some(e),
582 impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb::True, C, S> {
583 /// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields.
584 pub fn build_raw(self) -> Result<RawInvoice, CreationError> {
586 // If an error occurred at any time before, return it now
587 if let Some(e) = self.error {
592 currency: self.currency,
593 raw_amount: self.amount,
594 si_prefix: self.si_prefix,
597 let timestamp = self.timestamp.expect("ensured to be Some(t) by type T");
599 let tagged_fields = self.tagged_fields.into_iter().map(|tf| {
600 RawTaggedField::KnownSemantics(tf)
601 }).collect::<Vec<_>>();
603 let data = RawDataPart {
604 timestamp: timestamp,
605 tagged_fields: tagged_fields,
615 impl<H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<tb::False, H, T, C, S> {
616 /// Set the description. This function is only available if no description (hash) was set.
617 pub fn description(mut self, description: String) -> InvoiceBuilder<tb::True, H, T, C, S> {
618 match Description::new(description) {
619 Ok(d) => self.tagged_fields.push(TaggedField::Description(d)),
620 Err(e) => self.error = Some(e),
625 /// Set the description hash. This function is only available if no description (hash) was set.
626 pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder<tb::True, H, T, C, S> {
627 self.tagged_fields.push(TaggedField::DescriptionHash(Sha256(description_hash)));
632 impl<D: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, tb::False, T, C, S> {
633 /// Set the payment hash. This function is only available if no payment hash was set.
634 pub fn payment_hash(mut self, hash: sha256::Hash) -> InvoiceBuilder<D, tb::True, T, C, S> {
635 self.tagged_fields.push(TaggedField::PaymentHash(Sha256(hash)));
640 impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb::False, C, S> {
641 /// Sets the timestamp to a specific [`SystemTime`].
642 #[cfg(feature = "std")]
643 pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder<D, H, tb::True, C, S> {
644 match PositiveTimestamp::from_system_time(time) {
645 Ok(t) => self.timestamp = Some(t),
646 Err(e) => self.error = Some(e),
652 /// Sets the timestamp to a duration since the UNIX epoch.
653 pub fn duration_since_epoch(mut self, time: Duration) -> InvoiceBuilder<D, H, tb::True, C, S> {
654 match PositiveTimestamp::from_duration_since_epoch(time) {
655 Ok(t) => self.timestamp = Some(t),
656 Err(e) => self.error = Some(e),
662 /// Sets the timestamp to the current system time.
663 #[cfg(feature = "std")]
664 pub fn current_timestamp(mut self) -> InvoiceBuilder<D, H, tb::True, C, S> {
665 let now = PositiveTimestamp::from_system_time(SystemTime::now());
666 self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen"));
671 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, T, tb::False, S> {
672 /// Sets `min_final_cltv_expiry`.
673 pub fn min_final_cltv_expiry(mut self, min_final_cltv_expiry: u64) -> InvoiceBuilder<D, H, T, tb::True, S> {
674 self.tagged_fields.push(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry(min_final_cltv_expiry)));
679 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::False> {
680 /// Sets the payment secret and relevant features.
681 pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> InvoiceBuilder<D, H, T, C, tb::True> {
682 let features = InvoiceFeatures::empty()
683 .set_variable_length_onion_required()
684 .set_payment_secret_required();
685 self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret));
686 self.tagged_fields.push(TaggedField::Features(features));
691 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::True> {
692 /// Sets the `basic_mpp` feature as optional.
693 pub fn basic_mpp(mut self) -> Self {
694 self.tagged_fields = self.tagged_fields
696 .map(|field| match field {
697 TaggedField::Features(f) => TaggedField::Features(f.set_basic_mpp_optional()),
705 impl InvoiceBuilder<tb::True, tb::True, tb::True, tb::True, tb::True> {
706 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
707 /// and MUST produce a recoverable signature valid for the given hash and if applicable also for
708 /// the included payee public key.
709 pub fn build_signed<F>(self, sign_function: F) -> Result<Invoice, CreationError>
710 where F: FnOnce(&Message) -> RecoverableSignature
712 let invoice = self.try_build_signed::<_, ()>(|hash| {
713 Ok(sign_function(hash))
718 Err(SignOrCreationError::CreationError(e)) => Err(e),
719 Err(SignOrCreationError::SignError(())) => unreachable!(),
723 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY fail with
724 /// an error of type `E` and MUST produce a recoverable signature valid for the given hash and
725 /// if applicable also for the included payee public key.
726 pub fn try_build_signed<F, E>(self, sign_function: F) -> Result<Invoice, SignOrCreationError<E>>
727 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
729 let raw = match self.build_raw() {
731 Err(e) => return Err(SignOrCreationError::CreationError(e)),
734 let signed = match raw.sign(sign_function) {
736 Err(e) => return Err(SignOrCreationError::SignError(e)),
739 let invoice = Invoice {
740 signed_invoice: signed,
743 invoice.check_field_counts().expect("should be ensured by type signature of builder");
744 invoice.check_feature_bits().expect("should be ensured by type signature of builder");
745 invoice.check_amount().expect("should be ensured by type signature of builder");
752 impl SignedRawInvoice {
753 /// Disassembles the `SignedRawInvoice` into its three parts:
755 /// 2. hash of the raw invoice
757 pub fn into_parts(self) -> (RawInvoice, [u8; 32], InvoiceSignature) {
758 (self.raw_invoice, self.hash, self.signature)
761 /// The `RawInvoice` which was signed.
762 pub fn raw_invoice(&self) -> &RawInvoice {
766 /// The hash of the `RawInvoice` that was signed.
767 pub fn hash(&self) -> &[u8; 32] {
771 /// InvoiceSignature for the invoice.
772 pub fn signature(&self) -> &InvoiceSignature {
776 /// Recovers the public key used for signing the invoice from the recoverable signature.
777 pub fn recover_payee_pub_key(&self) -> Result<PayeePubKey, secp256k1::Error> {
778 let hash = Message::from_slice(&self.hash[..])
779 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
781 Ok(PayeePubKey(Secp256k1::new().recover(
787 /// Checks if the signature is valid for the included payee public key or if none exists if it's
788 /// valid for the recovered signature (which should always be true?).
789 pub fn check_signature(&self) -> bool {
790 let included_pub_key = self.raw_invoice.payee_pub_key();
792 let mut recovered_pub_key = Option::None;
793 if recovered_pub_key.is_none() {
794 let recovered = match self.recover_payee_pub_key() {
796 Err(_) => return false,
798 recovered_pub_key = Some(recovered);
801 let pub_key = included_pub_key.or_else(|| recovered_pub_key.as_ref())
802 .expect("One is always present");
804 let hash = Message::from_slice(&self.hash[..])
805 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
807 let secp_context = Secp256k1::new();
808 let verification_result = secp_context.verify(
810 &self.signature.to_standard(),
814 match verification_result {
821 /// Finds the first element of an enum stream of a given variant and extracts one member of the
822 /// variant. If no element was found `None` gets returned.
824 /// The following example would extract the first B.
833 /// let elements = vec![A(1), A(2), B(3), A(4)]
835 /// assert_eq!(find_extract!(elements.iter(), Enum::B(ref x), x), Some(3u16))
837 macro_rules! find_extract {
838 ($iter:expr, $enm:pat, $enm_var:ident) => {
839 find_all_extract!($iter, $enm, $enm_var).next()
843 /// Finds the all elements of an enum stream of a given variant and extracts one member of the
844 /// variant through an iterator.
846 /// The following example would extract all A.
855 /// let elements = vec![A(1), A(2), B(3), A(4)]
858 /// find_all_extract!(elements.iter(), Enum::A(ref x), x).collect::<Vec<u8>>(),
859 /// vec![1u8, 2u8, 4u8])
861 macro_rules! find_all_extract {
862 ($iter:expr, $enm:pat, $enm_var:ident) => {
863 $iter.filter_map(|tf| match *tf {
864 $enm => Some($enm_var),
870 #[allow(missing_docs)]
872 /// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
873 pub(crate) fn construct_invoice_preimage(hrp_bytes: &[u8], data_without_signature: &[u5]) -> Vec<u8> {
874 use bech32::FromBase32;
876 let mut preimage = Vec::<u8>::from(hrp_bytes);
878 let mut data_part = Vec::from(data_without_signature);
879 let overhang = (data_part.len() * 5) % 8;
881 // add padding if data does not end at a byte boundary
882 data_part.push(u5::try_from_u8(0).unwrap());
884 // if overhang is in (1..3) we need to add u5(0) padding two times
886 data_part.push(u5::try_from_u8(0).unwrap());
890 preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
891 .expect("No padding error may occur due to appended zero above."));
895 /// Hash the HRP as bytes and signatureless data part.
896 fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
897 let preimage = RawInvoice::construct_invoice_preimage(hrp_bytes, data_without_signature);
898 let mut hash: [u8; 32] = Default::default();
899 hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
903 /// Calculate the hash of the encoded `RawInvoice`
904 pub fn hash(&self) -> [u8; 32] {
905 use bech32::ToBase32;
907 RawInvoice::hash_from_parts(
908 self.hrp.to_string().as_bytes(),
909 &self.data.to_base32()
913 /// Signs the invoice using the supplied `sign_function`. This function MAY fail with an error
914 /// of type `E`. Since the signature of a `SignedRawInvoice` is not required to be valid there
915 /// are no constraints regarding the validity of the produced signature.
917 /// (C-not exported) As we don't currently support passing function pointers into methods
919 pub fn sign<F, E>(self, sign_method: F) -> Result<SignedRawInvoice, E>
920 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
922 let raw_hash = self.hash();
923 let hash = Message::from_slice(&raw_hash[..])
924 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
925 let signature = sign_method(&hash)?;
927 Ok(SignedRawInvoice {
930 signature: InvoiceSignature(signature),
934 /// Returns an iterator over all tagged fields with known semantics.
936 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
937 pub fn known_tagged_fields(&self)
938 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>>
940 // For 1.14.0 compatibility: closures' types can't be written an fn()->() in the
941 // function's type signature.
942 // TODO: refactor once impl Trait is available
943 fn match_raw(raw: &RawTaggedField) -> Option<&TaggedField> {
945 RawTaggedField::KnownSemantics(ref tf) => Some(tf),
950 self.data.tagged_fields.iter().filter_map(match_raw )
953 pub fn payment_hash(&self) -> Option<&Sha256> {
954 find_extract!(self.known_tagged_fields(), TaggedField::PaymentHash(ref x), x)
957 pub fn description(&self) -> Option<&Description> {
958 find_extract!(self.known_tagged_fields(), TaggedField::Description(ref x), x)
961 pub fn payee_pub_key(&self) -> Option<&PayeePubKey> {
962 find_extract!(self.known_tagged_fields(), TaggedField::PayeePubKey(ref x), x)
965 pub fn description_hash(&self) -> Option<&Sha256> {
966 find_extract!(self.known_tagged_fields(), TaggedField::DescriptionHash(ref x), x)
969 pub fn expiry_time(&self) -> Option<&ExpiryTime> {
970 find_extract!(self.known_tagged_fields(), TaggedField::ExpiryTime(ref x), x)
973 pub fn min_final_cltv_expiry(&self) -> Option<&MinFinalCltvExpiry> {
974 find_extract!(self.known_tagged_fields(), TaggedField::MinFinalCltvExpiry(ref x), x)
977 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
978 find_extract!(self.known_tagged_fields(), TaggedField::PaymentSecret(ref x), x)
981 pub fn features(&self) -> Option<&InvoiceFeatures> {
982 find_extract!(self.known_tagged_fields(), TaggedField::Features(ref x), x)
985 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
986 pub fn fallbacks(&self) -> Vec<&Fallback> {
987 find_all_extract!(self.known_tagged_fields(), TaggedField::Fallback(ref x), x).collect()
990 pub fn private_routes(&self) -> Vec<&PrivateRoute> {
991 find_all_extract!(self.known_tagged_fields(), TaggedField::PrivateRoute(ref x), x).collect()
994 pub fn amount_pico_btc(&self) -> Option<u64> {
995 self.hrp.raw_amount.map(|v| {
996 v * self.hrp.si_prefix.as_ref().map_or(1_000_000_000_000, |si| { si.multiplier() })
1000 pub fn currency(&self) -> Currency {
1001 self.hrp.currency.clone()
1005 impl PositiveTimestamp {
1006 /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
1007 /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1008 /// `CreationError::TimestampOutOfBounds`.
1009 pub fn from_unix_timestamp(unix_seconds: u64) -> Result<Self, CreationError> {
1010 if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
1011 Err(CreationError::TimestampOutOfBounds)
1013 Ok(PositiveTimestamp(Duration::from_secs(unix_seconds)))
1017 /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
1018 /// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1019 /// `CreationError::TimestampOutOfBounds`.
1020 #[cfg(feature = "std")]
1021 pub fn from_system_time(time: SystemTime) -> Result<Self, CreationError> {
1022 time.duration_since(SystemTime::UNIX_EPOCH)
1023 .map(Self::from_duration_since_epoch)
1024 .unwrap_or(Err(CreationError::TimestampOutOfBounds))
1027 /// Create a new `PositiveTimestamp` from a `Duration` since the UNIX epoch in
1028 /// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1029 /// `CreationError::TimestampOutOfBounds`.
1030 pub fn from_duration_since_epoch(duration: Duration) -> Result<Self, CreationError> {
1031 if duration.as_secs() <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
1032 Ok(PositiveTimestamp(duration))
1034 Err(CreationError::TimestampOutOfBounds)
1038 /// Returns the UNIX timestamp representing the stored time
1039 pub fn as_unix_timestamp(&self) -> u64 {
1043 /// Returns the duration of the stored time since the UNIX epoch
1044 pub fn as_duration_since_epoch(&self) -> Duration {
1048 /// Returns the `SystemTime` representing the stored time
1049 #[cfg(feature = "std")]
1050 pub fn as_time(&self) -> SystemTime {
1051 SystemTime::UNIX_EPOCH + self.0
1055 #[cfg(feature = "std")]
1056 impl Into<SystemTime> for PositiveTimestamp {
1057 fn into(self) -> SystemTime {
1058 SystemTime::UNIX_EPOCH + self.0
1063 /// Transform the `Invoice` into it's unchecked version
1064 pub fn into_signed_raw(self) -> SignedRawInvoice {
1068 /// Check that all mandatory fields are present
1069 fn check_field_counts(&self) -> Result<(), SemanticError> {
1070 // "A writer MUST include exactly one p field […]."
1071 let payment_hash_cnt = self.tagged_fields().filter(|&tf| match *tf {
1072 TaggedField::PaymentHash(_) => true,
1075 if payment_hash_cnt < 1 {
1076 return Err(SemanticError::NoPaymentHash);
1077 } else if payment_hash_cnt > 1 {
1078 return Err(SemanticError::MultiplePaymentHashes);
1081 // "A writer MUST include either exactly one d or exactly one h field."
1082 let description_cnt = self.tagged_fields().filter(|&tf| match *tf {
1083 TaggedField::Description(_) | TaggedField::DescriptionHash(_) => true,
1086 if description_cnt < 1 {
1087 return Err(SemanticError::NoDescription);
1088 } else if description_cnt > 1 {
1089 return Err(SemanticError::MultipleDescriptions);
1092 self.check_payment_secret()?;
1097 /// Checks that there is exactly one payment secret field
1098 fn check_payment_secret(&self) -> Result<(), SemanticError> {
1099 // "A writer MUST include exactly one `s` field."
1100 let payment_secret_count = self.tagged_fields().filter(|&tf| match *tf {
1101 TaggedField::PaymentSecret(_) => true,
1104 if payment_secret_count < 1 {
1105 return Err(SemanticError::NoPaymentSecret);
1106 } else if payment_secret_count > 1 {
1107 return Err(SemanticError::MultiplePaymentSecrets);
1113 /// Check that amount is a whole number of millisatoshis
1114 fn check_amount(&self) -> Result<(), SemanticError> {
1115 if let Some(amount_pico_btc) = self.amount_pico_btc() {
1116 if amount_pico_btc % 10 != 0 {
1117 return Err(SemanticError::ImpreciseAmount);
1123 /// Check that feature bits are set as required
1124 fn check_feature_bits(&self) -> Result<(), SemanticError> {
1125 self.check_payment_secret()?;
1127 // "A writer MUST set an s field if and only if the payment_secret feature is set."
1128 // (this requirement has been since removed, and we now require the payment secret
1129 // feature bit always).
1130 let features = self.tagged_fields().find(|&tf| match *tf {
1131 TaggedField::Features(_) => true,
1135 None => Err(SemanticError::InvalidFeatures),
1136 Some(TaggedField::Features(features)) => {
1137 if features.requires_unknown_bits() {
1138 Err(SemanticError::InvalidFeatures)
1139 } else if !features.supports_payment_secret() {
1140 Err(SemanticError::InvalidFeatures)
1145 Some(_) => unreachable!(),
1149 /// Check that the invoice is signed correctly and that key recovery works
1150 pub fn check_signature(&self) -> Result<(), SemanticError> {
1151 match self.signed_invoice.recover_payee_pub_key() {
1152 Err(secp256k1::Error::InvalidRecoveryId) =>
1153 return Err(SemanticError::InvalidRecoveryId),
1154 Err(secp256k1::Error::InvalidSignature) =>
1155 return Err(SemanticError::InvalidSignature),
1156 Err(e) => panic!("no other error may occur, got {:?}", e),
1160 if !self.signed_invoice.check_signature() {
1161 return Err(SemanticError::InvalidSignature);
1167 /// Constructs an `Invoice` from a `SignedRawInvoice` by checking all its invariants.
1169 /// use lightning_invoice::*;
1171 /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
1172 /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
1173 /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
1174 /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
1175 /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
1176 /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
1177 /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
1178 /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
1179 /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
1180 /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
1181 /// j5r6drg6k6zcqj0fcwg";
1183 /// let signed = invoice.parse::<SignedRawInvoice>().unwrap();
1185 /// assert!(Invoice::from_signed(signed).is_ok());
1187 pub fn from_signed(signed_invoice: SignedRawInvoice) -> Result<Self, SemanticError> {
1188 let invoice = Invoice {
1189 signed_invoice: signed_invoice,
1191 invoice.check_field_counts()?;
1192 invoice.check_feature_bits()?;
1193 invoice.check_signature()?;
1194 invoice.check_amount()?;
1199 /// Returns the `Invoice`'s timestamp (should equal its creation time)
1200 #[cfg(feature = "std")]
1201 pub fn timestamp(&self) -> SystemTime {
1202 self.signed_invoice.raw_invoice().data.timestamp.as_time()
1205 /// Returns the `Invoice`'s timestamp as a duration since the UNIX epoch
1206 pub fn duration_since_epoch(&self) -> Duration {
1207 self.signed_invoice.raw_invoice().data.timestamp.0
1210 /// Returns an iterator over all tagged fields of this Invoice.
1212 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
1213 pub fn tagged_fields(&self)
1214 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>> {
1215 self.signed_invoice.raw_invoice().known_tagged_fields()
1218 /// Returns the hash to which we will receive the preimage on completion of the payment
1219 pub fn payment_hash(&self) -> &sha256::Hash {
1220 &self.signed_invoice.payment_hash().expect("checked by constructor").0
1223 /// Return the description or a hash of it for longer ones
1225 /// (C-not exported) because we don't yet export InvoiceDescription
1226 pub fn description(&self) -> InvoiceDescription {
1227 if let Some(ref direct) = self.signed_invoice.description() {
1228 return InvoiceDescription::Direct(direct);
1229 } else if let Some(ref hash) = self.signed_invoice.description_hash() {
1230 return InvoiceDescription::Hash(hash);
1232 unreachable!("ensured by constructor");
1235 /// Get the payee's public key if one was included in the invoice
1236 pub fn payee_pub_key(&self) -> Option<&PublicKey> {
1237 self.signed_invoice.payee_pub_key().map(|x| &x.0)
1240 /// Get the payment secret if one was included in the invoice
1241 pub fn payment_secret(&self) -> &PaymentSecret {
1242 self.signed_invoice.payment_secret().expect("was checked by constructor")
1245 /// Get the invoice features if they were included in the invoice
1246 pub fn features(&self) -> Option<&InvoiceFeatures> {
1247 self.signed_invoice.features()
1250 /// Recover the payee's public key (only to be used if none was included in the invoice)
1251 pub fn recover_payee_pub_key(&self) -> PublicKey {
1252 self.signed_invoice.recover_payee_pub_key().expect("was checked by constructor").0
1255 /// Returns the invoice's expiry time, if present, otherwise [`DEFAULT_EXPIRY_TIME`].
1256 pub fn expiry_time(&self) -> Duration {
1257 self.signed_invoice.expiry_time()
1259 .unwrap_or(Duration::from_secs(DEFAULT_EXPIRY_TIME))
1262 /// Returns whether the invoice has expired.
1263 #[cfg(feature = "std")]
1264 pub fn is_expired(&self) -> bool {
1265 Self::is_expired_from_epoch(&self.timestamp(), self.expiry_time())
1268 /// Returns whether the expiry time from the given epoch has passed.
1269 #[cfg(feature = "std")]
1270 pub(crate) fn is_expired_from_epoch(epoch: &SystemTime, expiry_time: Duration) -> bool {
1271 match epoch.elapsed() {
1272 Ok(elapsed) => elapsed > expiry_time,
1277 /// Returns whether the expiry time would pass at the given point in time.
1278 /// `at_time` is the timestamp as a duration since the UNIX epoch.
1279 pub fn would_expire(&self, at_time: Duration) -> bool {
1280 self.duration_since_epoch() + self.expiry_time() < at_time
1283 /// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
1284 /// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
1285 pub fn min_final_cltv_expiry(&self) -> u64 {
1286 self.signed_invoice.min_final_cltv_expiry()
1288 .unwrap_or(DEFAULT_MIN_FINAL_CLTV_EXPIRY)
1291 /// Returns a list of all fallback addresses
1293 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
1294 pub fn fallbacks(&self) -> Vec<&Fallback> {
1295 self.signed_invoice.fallbacks()
1298 /// Returns a list of all routes included in the invoice
1299 pub fn private_routes(&self) -> Vec<&PrivateRoute> {
1300 self.signed_invoice.private_routes()
1303 /// Returns a list of all routes included in the invoice as the underlying hints
1304 pub fn route_hints(&self) -> Vec<RouteHint> {
1306 self.signed_invoice.known_tagged_fields(), TaggedField::PrivateRoute(ref x), x
1307 ).map(|route| (**route).clone()).collect()
1310 /// Returns the currency for which the invoice was issued
1311 pub fn currency(&self) -> Currency {
1312 self.signed_invoice.currency()
1315 /// Returns the amount if specified in the invoice as millisatoshis.
1316 pub fn amount_milli_satoshis(&self) -> Option<u64> {
1317 self.signed_invoice.amount_pico_btc().map(|v| v / 10)
1320 /// Returns the amount if specified in the invoice as pico <currency>.
1321 fn amount_pico_btc(&self) -> Option<u64> {
1322 self.signed_invoice.amount_pico_btc()
1326 impl From<TaggedField> for RawTaggedField {
1327 fn from(tf: TaggedField) -> Self {
1328 RawTaggedField::KnownSemantics(tf)
1333 /// Numeric representation of the field's tag
1334 pub fn tag(&self) -> u5 {
1335 let tag = match *self {
1336 TaggedField::PaymentHash(_) => constants::TAG_PAYMENT_HASH,
1337 TaggedField::Description(_) => constants::TAG_DESCRIPTION,
1338 TaggedField::PayeePubKey(_) => constants::TAG_PAYEE_PUB_KEY,
1339 TaggedField::DescriptionHash(_) => constants::TAG_DESCRIPTION_HASH,
1340 TaggedField::ExpiryTime(_) => constants::TAG_EXPIRY_TIME,
1341 TaggedField::MinFinalCltvExpiry(_) => constants::TAG_MIN_FINAL_CLTV_EXPIRY,
1342 TaggedField::Fallback(_) => constants::TAG_FALLBACK,
1343 TaggedField::PrivateRoute(_) => constants::TAG_PRIVATE_ROUTE,
1344 TaggedField::PaymentSecret(_) => constants::TAG_PAYMENT_SECRET,
1345 TaggedField::Features(_) => constants::TAG_FEATURES,
1348 u5::try_from_u8(tag).expect("all tags defined are <32")
1354 /// Creates a new `Description` if `description` is at most 1023 __bytes__ long,
1355 /// returns `CreationError::DescriptionTooLong` otherwise
1357 /// Please note that single characters may use more than one byte due to UTF8 encoding.
1358 pub fn new(description: String) -> Result<Description, CreationError> {
1359 if description.len() > 639 {
1360 Err(CreationError::DescriptionTooLong)
1362 Ok(Description(description))
1366 /// Returns the underlying description `String`
1367 pub fn into_inner(self) -> String {
1372 impl Into<String> for Description {
1373 fn into(self) -> String {
1378 impl Deref for Description {
1381 fn deref(&self) -> &str {
1386 impl From<PublicKey> for PayeePubKey {
1387 fn from(pk: PublicKey) -> Self {
1392 impl Deref for PayeePubKey {
1393 type Target = PublicKey;
1395 fn deref(&self) -> &PublicKey {
1401 /// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1402 /// overflow on adding the `EpiryTime` to it then this function will return a
1403 /// `CreationError::ExpiryTimeOutOfBounds`.
1404 pub fn from_seconds(seconds: u64) -> Result<ExpiryTime, CreationError> {
1405 if seconds <= MAX_EXPIRY_TIME {
1406 Ok(ExpiryTime(Duration::from_secs(seconds)))
1408 Err(CreationError::ExpiryTimeOutOfBounds)
1412 /// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1413 /// would overflow on adding the `EpiryTime` to it then this function will return a
1414 /// `CreationError::ExpiryTimeOutOfBounds`.
1415 pub fn from_duration(duration: Duration) -> Result<ExpiryTime, CreationError> {
1416 if duration.as_secs() <= MAX_EXPIRY_TIME {
1417 Ok(ExpiryTime(duration))
1419 Err(CreationError::ExpiryTimeOutOfBounds)
1423 /// Returns the expiry time in seconds
1424 pub fn as_seconds(&self) -> u64 {
1428 /// Returns a reference to the underlying `Duration` (=expiry time)
1429 pub fn as_duration(&self) -> &Duration {
1435 /// Creates a new (partial) route from a list of hops
1436 pub fn new(hops: RouteHint) -> Result<PrivateRoute, CreationError> {
1437 if hops.0.len() <= 12 {
1438 Ok(PrivateRoute(hops))
1440 Err(CreationError::RouteTooLong)
1444 /// Returns the underlying list of hops
1445 pub fn into_inner(self) -> RouteHint {
1450 impl Into<RouteHint> for PrivateRoute {
1451 fn into(self) -> RouteHint {
1456 impl Deref for PrivateRoute {
1457 type Target = RouteHint;
1459 fn deref(&self) -> &RouteHint {
1464 impl Deref for InvoiceSignature {
1465 type Target = RecoverableSignature;
1467 fn deref(&self) -> &RecoverableSignature {
1472 impl Deref for SignedRawInvoice {
1473 type Target = RawInvoice;
1475 fn deref(&self) -> &RawInvoice {
1480 /// Errors that may occur when constructing a new `RawInvoice` or `Invoice`
1481 #[derive(Eq, PartialEq, Debug, Clone)]
1482 pub enum CreationError {
1483 /// The supplied description string was longer than 639 __bytes__ (see [`Description::new(…)`](./struct.Description.html#method.new))
1486 /// The specified route has too many hops and can't be encoded
1489 /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1490 TimestampOutOfBounds,
1492 /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1493 ExpiryTimeOutOfBounds,
1495 /// The supplied millisatoshi amount was greater than the total bitcoin supply.
1499 impl Display for CreationError {
1500 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1502 CreationError::DescriptionTooLong => f.write_str("The supplied description string was longer than 639 bytes"),
1503 CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
1504 CreationError::TimestampOutOfBounds => f.write_str("The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`"),
1505 CreationError::ExpiryTimeOutOfBounds => f.write_str("The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`"),
1506 CreationError::InvalidAmount => f.write_str("The supplied millisatoshi amount was greater than the total bitcoin supply"),
1511 #[cfg(feature = "std")]
1512 impl std::error::Error for CreationError { }
1514 /// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
1515 /// requirements sections in BOLT #11
1516 #[derive(Eq, PartialEq, Debug, Clone)]
1517 pub enum SemanticError {
1518 /// The invoice is missing the mandatory payment hash
1521 /// The invoice has multiple payment hashes which isn't allowed
1522 MultiplePaymentHashes,
1524 /// No description or description hash are part of the invoice
1527 /// The invoice contains multiple descriptions and/or description hashes which isn't allowed
1528 MultipleDescriptions,
1530 /// The invoice is missing the mandatory payment secret, which all modern lightning nodes
1534 /// The invoice contains multiple payment secrets
1535 MultiplePaymentSecrets,
1537 /// The invoice's features are invalid
1540 /// The recovery id doesn't fit the signature/pub key
1543 /// The invoice's signature is invalid
1546 /// The invoice's amount was not a whole number of millisatoshis
1550 impl Display for SemanticError {
1551 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1553 SemanticError::NoPaymentHash => f.write_str("The invoice is missing the mandatory payment hash"),
1554 SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
1555 SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
1556 SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
1557 SemanticError::NoPaymentSecret => f.write_str("The invoice is missing the mandatory payment secret"),
1558 SemanticError::MultiplePaymentSecrets => f.write_str("The invoice contains multiple payment secrets"),
1559 SemanticError::InvalidFeatures => f.write_str("The invoice's features are invalid"),
1560 SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
1561 SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
1562 SemanticError::ImpreciseAmount => f.write_str("The invoice's amount was not a whole number of millisatoshis"),
1567 #[cfg(feature = "std")]
1568 impl std::error::Error for SemanticError { }
1570 /// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
1572 #[derive(Eq, PartialEq, Debug, Clone)]
1573 pub enum SignOrCreationError<S = ()> {
1574 /// An error occurred during signing
1577 /// An error occurred while building the transaction
1578 CreationError(CreationError),
1581 impl<S> Display for SignOrCreationError<S> {
1582 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1584 SignOrCreationError::SignError(_) => f.write_str("An error occurred during signing"),
1585 SignOrCreationError::CreationError(err) => err.fmt(f),
1592 use bitcoin_hashes::hex::FromHex;
1593 use bitcoin_hashes::sha256;
1596 fn test_system_time_bounds_assumptions() {
1600 ::PositiveTimestamp::from_unix_timestamp(::SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1),
1601 Err(::CreationError::TimestampOutOfBounds)
1605 ::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
1606 Err(::CreationError::ExpiryTimeOutOfBounds)
1611 fn test_calc_invoice_hash() {
1612 use ::{RawInvoice, RawHrp, RawDataPart, Currency, PositiveTimestamp};
1613 use ::TaggedField::*;
1615 let invoice = RawInvoice {
1617 currency: Currency::Bitcoin,
1622 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1623 tagged_fields: vec![
1624 PaymentHash(::Sha256(sha256::Hash::from_hex(
1625 "0001020304050607080900010203040506070809000102030405060708090102"
1626 ).unwrap())).into(),
1627 Description(::Description::new(
1628 "Please consider supporting this project".to_owned()
1634 let expected_hash = [
1635 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27, 0x7b, 0x1d,
1636 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7, 0x83, 0x5d, 0xb2, 0xec,
1637 0xd5, 0x18, 0xe1, 0xc9
1640 assert_eq!(invoice.hash(), expected_hash)
1644 fn test_check_signature() {
1646 use secp256k1::Secp256k1;
1647 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1648 use secp256k1::key::{SecretKey, PublicKey};
1649 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1652 let invoice = SignedRawInvoice {
1653 raw_invoice: RawInvoice {
1655 currency: Currency::Bitcoin,
1660 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1661 tagged_fields: vec ! [
1662 PaymentHash(Sha256(sha256::Hash::from_hex(
1663 "0001020304050607080900010203040506070809000102030405060708090102"
1664 ).unwrap())).into(),
1667 "Please consider supporting this project".to_owned()
1674 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1675 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1676 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1678 signature: InvoiceSignature(RecoverableSignature::from_compact(
1680 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1681 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1682 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1683 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1684 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1685 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1687 RecoveryId::from_i32(0).unwrap()
1691 assert!(invoice.check_signature());
1693 let private_key = SecretKey::from_slice(
1695 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1696 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1697 0x3b, 0x2d, 0xb7, 0x34
1700 let public_key = PublicKey::from_secret_key(&Secp256k1::new(), &private_key);
1702 assert_eq!(invoice.recover_payee_pub_key(), Ok(::PayeePubKey(public_key)));
1704 let (raw_invoice, _, _) = invoice.into_parts();
1705 let new_signed = raw_invoice.sign::<_, ()>(|hash| {
1706 Ok(Secp256k1::new().sign_recoverable(hash, &private_key))
1709 assert!(new_signed.check_signature());
1713 fn test_check_feature_bits() {
1715 use lightning::ln::features::InvoiceFeatures;
1716 use secp256k1::Secp256k1;
1717 use secp256k1::key::SecretKey;
1718 use {RawInvoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp, Invoice,
1721 let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
1722 let payment_secret = lightning::ln::PaymentSecret([21; 32]);
1723 let invoice_template = RawInvoice {
1725 currency: Currency::Bitcoin,
1730 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1731 tagged_fields: vec ! [
1732 PaymentHash(Sha256(sha256::Hash::from_hex(
1733 "0001020304050607080900010203040506070809000102030405060708090102"
1734 ).unwrap())).into(),
1737 "Please consider supporting this project".to_owned()
1746 let mut invoice = invoice_template.clone();
1747 invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
1748 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1750 assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
1752 // Missing feature bits
1754 let mut invoice = invoice_template.clone();
1755 invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
1756 invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
1757 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1759 assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
1761 // Including payment secret and feature bits
1763 let mut invoice = invoice_template.clone();
1764 invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
1765 invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
1766 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1768 assert!(Invoice::from_signed(invoice).is_ok());
1770 // No payment secret or features
1772 let invoice = invoice_template.clone();
1773 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1775 assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
1777 // No payment secret or feature bits
1779 let mut invoice = invoice_template.clone();
1780 invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
1781 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1783 assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
1785 // Missing payment secret
1787 let mut invoice = invoice_template.clone();
1788 invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
1789 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1791 assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
1793 // Multiple payment secrets
1795 let mut invoice = invoice_template.clone();
1796 invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
1797 invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
1798 invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
1800 assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::MultiplePaymentSecrets));
1804 fn test_builder_amount() {
1807 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1808 .description("Test".into())
1809 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1810 .duration_since_epoch(Duration::from_secs(1234567));
1812 let invoice = builder.clone()
1813 .amount_milli_satoshis(1500)
1817 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Nano));
1818 assert_eq!(invoice.hrp.raw_amount, Some(15));
1821 let invoice = builder.clone()
1822 .amount_milli_satoshis(150)
1826 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Pico));
1827 assert_eq!(invoice.hrp.raw_amount, Some(1500));
1831 fn test_builder_fail() {
1833 use lightning::routing::router::RouteHintHop;
1834 use std::iter::FromIterator;
1835 use secp256k1::key::PublicKey;
1837 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1838 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1839 .duration_since_epoch(Duration::from_secs(1234567))
1840 .min_final_cltv_expiry(144);
1842 let too_long_string = String::from_iter(
1843 (0..1024).map(|_| '?')
1846 let long_desc_res = builder.clone()
1847 .description(too_long_string)
1849 assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
1851 let route_hop = RouteHintHop {
1852 src_node_id: PublicKey::from_slice(
1854 0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
1855 0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
1856 0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
1859 short_channel_id: 0,
1862 proportional_millionths: 0,
1864 cltv_expiry_delta: 0,
1865 htlc_minimum_msat: None,
1866 htlc_maximum_msat: None,
1868 let too_long_route = RouteHint(vec![route_hop; 13]);
1869 let long_route_res = builder.clone()
1870 .description("Test".into())
1871 .private_route(too_long_route)
1873 assert_eq!(long_route_res, Err(CreationError::RouteTooLong));
1875 let sign_error_res = builder.clone()
1876 .description("Test".into())
1877 .payment_secret(PaymentSecret([0; 32]))
1878 .try_build_signed(|_| {
1879 Err("ImaginaryError")
1881 assert_eq!(sign_error_res, Err(SignOrCreationError::SignError("ImaginaryError")));
1885 fn test_builder_ok() {
1887 use lightning::routing::router::RouteHintHop;
1888 use secp256k1::Secp256k1;
1889 use secp256k1::key::{SecretKey, PublicKey};
1890 use std::time::{UNIX_EPOCH, Duration};
1892 let secp_ctx = Secp256k1::new();
1894 let private_key = SecretKey::from_slice(
1896 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1897 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1898 0x3b, 0x2d, 0xb7, 0x34
1901 let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
1903 let route_1 = RouteHint(vec![
1905 src_node_id: public_key.clone(),
1906 short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
1909 proportional_millionths: 1,
1911 cltv_expiry_delta: 145,
1912 htlc_minimum_msat: None,
1913 htlc_maximum_msat: None,
1916 src_node_id: public_key.clone(),
1917 short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
1920 proportional_millionths: 2,
1922 cltv_expiry_delta: 146,
1923 htlc_minimum_msat: None,
1924 htlc_maximum_msat: None,
1928 let route_2 = RouteHint(vec![
1930 src_node_id: public_key.clone(),
1931 short_channel_id: 0,
1934 proportional_millionths: 3,
1936 cltv_expiry_delta: 147,
1937 htlc_minimum_msat: None,
1938 htlc_maximum_msat: None,
1941 src_node_id: public_key.clone(),
1942 short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
1945 proportional_millionths: 4,
1947 cltv_expiry_delta: 148,
1948 htlc_minimum_msat: None,
1949 htlc_maximum_msat: None,
1953 let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
1954 .amount_milli_satoshis(123)
1955 .duration_since_epoch(Duration::from_secs(1234567))
1956 .payee_pub_key(public_key.clone())
1957 .expiry_time(Duration::from_secs(54321))
1958 .min_final_cltv_expiry(144)
1959 .fallback(Fallback::PubKeyHash([0;20]))
1960 .private_route(route_1.clone())
1961 .private_route(route_2.clone())
1962 .description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
1963 .payment_hash(sha256::Hash::from_slice(&[21;32][..]).unwrap())
1964 .payment_secret(PaymentSecret([42; 32]))
1967 let invoice = builder.clone().build_signed(|hash| {
1968 secp_ctx.sign_recoverable(hash, &private_key)
1971 assert!(invoice.check_signature().is_ok());
1972 assert_eq!(invoice.tagged_fields().count(), 10);
1974 assert_eq!(invoice.amount_milli_satoshis(), Some(123));
1975 assert_eq!(invoice.amount_pico_btc(), Some(1230));
1976 assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
1977 #[cfg(feature = "std")]
1979 invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),
1982 assert_eq!(invoice.payee_pub_key(), Some(&public_key));
1983 assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
1984 assert_eq!(invoice.min_final_cltv_expiry(), 144);
1985 assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
1986 assert_eq!(invoice.private_routes(), vec![&PrivateRoute(route_1), &PrivateRoute(route_2)]);
1988 invoice.description(),
1989 InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
1991 assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&[21;32][..]).unwrap());
1992 assert_eq!(invoice.payment_secret(), &PaymentSecret([42; 32]));
1993 assert_eq!(invoice.features(), Some(&InvoiceFeatures::known()));
1995 let raw_invoice = builder.build_raw().unwrap();
1996 assert_eq!(raw_invoice, *invoice.into_signed_raw().raw_invoice())
2000 fn test_default_values() {
2002 use secp256k1::Secp256k1;
2003 use secp256k1::key::SecretKey;
2005 let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
2006 .description("Test".into())
2007 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
2008 .payment_secret(PaymentSecret([0; 32]))
2009 .duration_since_epoch(Duration::from_secs(1234567))
2012 .sign::<_, ()>(|hash| {
2013 let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
2014 let secp_ctx = Secp256k1::new();
2015 Ok(secp_ctx.sign_recoverable(hash, &privkey))
2018 let invoice = Invoice::from_signed(signed_invoice).unwrap();
2020 assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
2021 assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));
2022 assert!(!invoice.would_expire(Duration::from_secs(1234568)));
2026 fn test_expiration() {
2028 use secp256k1::Secp256k1;
2029 use secp256k1::key::SecretKey;
2031 let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
2032 .description("Test".into())
2033 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
2034 .payment_secret(PaymentSecret([0; 32]))
2035 .duration_since_epoch(Duration::from_secs(1234567))
2038 .sign::<_, ()>(|hash| {
2039 let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
2040 let secp_ctx = Secp256k1::new();
2041 Ok(secp_ctx.sign_recoverable(hash, &privkey))
2044 let invoice = Invoice::from_signed(signed_invoice).unwrap();
2046 assert!(invoice.would_expire(Duration::from_secs(1234567 + DEFAULT_EXPIRY_TIME + 1)));