2 #![deny(non_upper_case_globals)]
3 #![deny(non_camel_case_types)]
4 #![deny(non_snake_case)]
7 #![cfg_attr(feature = "strict", deny(warnings))]
9 //! This crate provides data structures to represent
10 //! [lightning BOLT11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md)
11 //! invoices and functions to create, encode and decode these. If you just want to use the standard
12 //! en-/decoding functionality this should get you started:
14 //! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
15 //! * For constructing invoices use the `InvoiceBuilder`
16 //! * For serializing invoices use the `Display`/`ToString` traits
20 extern crate bitcoin_hashes;
21 extern crate lightning;
22 extern crate num_traits;
23 extern crate secp256k1;
26 use bitcoin_hashes::Hash;
27 use bitcoin_hashes::sha256;
28 use lightning::ln::PaymentSecret;
29 use lightning::ln::features::InvoiceFeatures;
30 #[cfg(any(doc, test))]
31 use lightning::routing::network_graph::RoutingFees;
32 use lightning::routing::router::RouteHintHop;
34 use secp256k1::key::PublicKey;
35 use secp256k1::{Message, Secp256k1};
36 use secp256k1::recovery::RecoverableSignature;
38 use std::fmt::{Display, Formatter, self};
39 use std::iter::FilterMap;
42 use std::time::{SystemTime, Duration, UNIX_EPOCH};
48 pub use de::{ParseError, ParseOrSemanticError};
50 // TODO: fix before 2037 (see rust PR #55527)
51 /// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
52 /// one of the unit tests, please run them.
53 const SYSTEM_TIME_MAX_UNIX_TIMESTAMP: u64 = std::i32::MAX as u64;
55 /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
56 /// it should be rather low as long as we still have to support 32bit time representations
57 const MAX_EXPIRY_TIME: u64 = 60 * 60 * 24 * 356;
59 /// Default expiry time as defined by [BOLT 11].
61 /// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
62 const DEFAULT_EXPIRY_TIME: u64 = 3600;
64 /// Default minimum final CLTV expiry as defined by [BOLT 11].
66 /// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
67 const DEFAULT_MIN_FINAL_CLTV_EXPIRY: u64 = 18;
69 /// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
70 /// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
71 /// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
72 /// please open an issue. If all tests pass you should be able to use this library safely by just
73 /// removing this function till we patch it accordingly.
74 fn __system_time_size_check() {
75 // Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
76 // a `Duration` since `SystemTime::UNIX_EPOCH`.
77 unsafe { std::mem::transmute_copy::<SystemTime, [u8; 16]>(&UNIX_EPOCH); }
81 /// **Call this function on startup to ensure that all assumptions about the platform are valid.**
83 /// Unfortunately we have to make assumptions about the upper bounds of the `SystemTime` type on
84 /// your platform which we can't fully verify at compile time and which isn't part of it's contract.
85 /// To our best knowledge our assumptions hold for all platforms officially supported by rust, but
86 /// since this check is fast we recommend to do it anyway.
88 /// If this function fails this is considered a bug. Please open an issue describing your
89 /// platform and stating your current system time.
92 /// If the check fails this function panics. By calling this function on startup you ensure that
93 /// this wont happen at an arbitrary later point in time.
94 pub fn check_platform() {
95 // The upper and lower bounds of `SystemTime` are not part of its public contract and are
96 // platform specific. That's why we have to test if our assumptions regarding these bounds
97 // hold on the target platform.
99 // If this test fails on your platform, please don't use the library and open an issue
100 // instead so we can resolve the situation. Currently this library is tested on:
102 let fail_date = UNIX_EPOCH + Duration::from_secs(SYSTEM_TIME_MAX_UNIX_TIMESTAMP);
103 let year = Duration::from_secs(60 * 60 * 24 * 365);
105 // Make sure that the library will keep working for another year
106 assert!(fail_date.duration_since(SystemTime::now()).unwrap() > year);
108 let max_ts = PositiveTimestamp::from_unix_timestamp(
109 SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
111 let max_exp = ::ExpiryTime::from_seconds(MAX_EXPIRY_TIME).unwrap();
114 (*max_ts.as_time() + *max_exp.as_duration()).duration_since(UNIX_EPOCH).unwrap().as_secs(),
115 SYSTEM_TIME_MAX_UNIX_TIMESTAMP
120 /// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
121 /// that only a semantically and syntactically correct Invoice can be built using it.
124 /// extern crate secp256k1;
125 /// extern crate lightning_invoice;
126 /// extern crate bitcoin_hashes;
128 /// use bitcoin_hashes::Hash;
129 /// use bitcoin_hashes::sha256;
131 /// use secp256k1::Secp256k1;
132 /// use secp256k1::key::SecretKey;
134 /// use lightning_invoice::{Currency, InvoiceBuilder};
137 /// let private_key = SecretKey::from_slice(
139 /// 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f,
140 /// 0xe2, 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04,
141 /// 0xa8, 0xca, 0x3b, 0x2d, 0xb7, 0x34
145 /// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
147 /// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
148 /// .description("Coins pls!".into())
149 /// .payment_hash(payment_hash)
150 /// .current_timestamp()
151 /// .min_final_cltv_expiry(144)
152 /// .build_signed(|hash| {
153 /// Secp256k1::new().sign_recoverable(hash, &private_key)
157 /// assert!(invoice.to_string().starts_with("lnbc1"));
161 /// # Type parameters
162 /// The two parameters `D` and `H` signal if the builder already contains the correct amount of the
164 /// * `D`: exactly one `Description` or `DescriptionHash`
165 /// * `H`: exactly one `PaymentHash`
166 /// * `T`: the timestamp is set
168 /// (C-not exported) as we likely need to manually select one set of boolean type parameters.
169 #[derive(Eq, PartialEq, Debug, Clone)]
170 pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> {
173 si_prefix: Option<SiPrefix>,
174 timestamp: Option<PositiveTimestamp>,
175 tagged_fields: Vec<TaggedField>,
176 error: Option<CreationError>,
178 phantom_d: std::marker::PhantomData<D>,
179 phantom_h: std::marker::PhantomData<H>,
180 phantom_t: std::marker::PhantomData<T>,
181 phantom_c: std::marker::PhantomData<C>,
184 /// Represents a syntactically and semantically correct lightning BOLT11 invoice.
186 /// There are three ways to construct an `Invoice`:
187 /// 1. using `InvoiceBuilder`
188 /// 2. using `Invoice::from_signed(SignedRawInvoice)`
189 /// 3. using `str::parse::<Invoice>(&str)`
190 #[derive(Eq, PartialEq, Debug, Clone)]
192 signed_invoice: SignedRawInvoice,
195 /// Represents the description of an invoice which has to be either a directly included string or
196 /// a hash of a description provided out of band.
198 /// (C-not exported) As we don't have a good way to map the reference lifetimes making this
199 /// practically impossible to use safely in languages like C.
200 #[derive(Eq, PartialEq, Debug, Clone)]
201 pub enum InvoiceDescription<'f> {
202 /// Reference to the directly supplied description in the invoice
203 Direct(&'f Description),
205 /// Reference to the description's hash included in the invoice
209 /// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be
213 /// The hash has to be either from the deserialized invoice or from the serialized `raw_invoice`.
214 #[derive(Eq, PartialEq, Debug, Clone)]
215 pub struct SignedRawInvoice {
216 /// The rawInvoice that the signature belongs to
217 raw_invoice: RawInvoice,
219 /// Hash of the `RawInvoice` that will be used to check the signature.
221 /// * if the `SignedRawInvoice` was deserialized the hash is of from the original encoded form,
222 /// since it's not guaranteed that encoding it again will lead to the same result since integers
223 /// could have been encoded with leading zeroes etc.
224 /// * if the `SignedRawInvoice` was constructed manually the hash will be the calculated hash
225 /// from the `RawInvoice`
228 /// signature of the payment request
229 signature: InvoiceSignature,
232 /// Represents an syntactically correct Invoice for a payment on the lightning network,
233 /// but without the signature information.
234 /// De- and encoding should not lead to information loss but may lead to different hashes.
236 /// For methods without docs see the corresponding methods in `Invoice`.
237 #[derive(Eq, PartialEq, Debug, Clone)]
238 pub struct RawInvoice {
239 /// human readable part
243 pub data: RawDataPart,
246 /// Data of the `RawInvoice` that is encoded in the human readable part
248 /// (C-not exported) As we don't yet support Option<Enum>
249 #[derive(Eq, PartialEq, Debug, Clone)]
251 /// The currency deferred from the 3rd and 4th character of the bech32 transaction
252 pub currency: Currency,
254 /// The amount that, multiplied by the SI prefix, has to be payed
255 pub raw_amount: Option<u64>,
257 /// SI prefix that gets multiplied with the `raw_amount`
258 pub si_prefix: Option<SiPrefix>,
261 /// Data of the `RawInvoice` that is encoded in the data part
262 #[derive(Eq, PartialEq, Debug, Clone)]
263 pub struct RawDataPart {
264 /// generation time of the invoice
265 pub timestamp: PositiveTimestamp,
267 /// tagged fields of the payment request
268 pub tagged_fields: Vec<RawTaggedField>,
271 /// A timestamp that refers to a date after 1 January 1970 which means its representation as UNIX
272 /// timestamp is positive.
275 /// The UNIX timestamp representing the stored time has to be positive and small enough so that
276 /// a `EpiryTime` can be added to it without an overflow.
277 #[derive(Eq, PartialEq, Debug, Clone)]
278 pub struct PositiveTimestamp(SystemTime);
280 /// SI prefixes for the human readable part
281 #[derive(Eq, PartialEq, Debug, Clone, Copy)]
294 /// Returns the multiplier to go from a BTC value to picoBTC implied by this SiPrefix.
295 /// This is effectively 10^12 * the prefix multiplier
296 pub fn multiplier(&self) -> u64 {
298 SiPrefix::Milli => 1_000_000_000,
299 SiPrefix::Micro => 1_000_000,
300 SiPrefix::Nano => 1_000,
305 /// Returns all enum variants of `SiPrefix` sorted in descending order of their associated
308 /// (C-not exported) As we don't yet support a slice of enums, and also because this function
309 /// isn't the most critical to expose.
310 pub fn values_desc() -> &'static [SiPrefix] {
312 static VALUES: [SiPrefix; 4] = [Milli, Micro, Nano, Pico];
317 /// Enum representing the crypto currencies (or networks) supported by this library
318 #[derive(Eq, PartialEq, Debug, Clone)]
329 /// Bitcoin simnet/signet
333 /// Tagged field which may have an unknown tag
334 #[derive(Eq, PartialEq, Debug, Clone)]
335 pub enum RawTaggedField {
336 /// Parsed tagged field with known tag
337 KnownSemantics(TaggedField),
338 /// tagged field which was not parsed due to an unknown tag or undefined field semantics
339 UnknownSemantics(Vec<u5>),
342 /// Tagged field with known tag
344 /// For descriptions of the enum values please refer to the enclosed type's docs.
345 #[allow(missing_docs)]
346 #[derive(Eq, PartialEq, Debug, Clone)]
347 pub enum TaggedField {
349 Description(Description),
350 PayeePubKey(PayeePubKey),
351 DescriptionHash(Sha256),
352 ExpiryTime(ExpiryTime),
353 MinFinalCltvExpiry(MinFinalCltvExpiry),
356 PaymentSecret(PaymentSecret),
357 Features(InvoiceFeatures),
361 #[derive(Eq, PartialEq, Debug, Clone)]
362 pub struct Sha256(pub sha256::Hash);
364 /// Description string
367 /// The description can be at most 639 __bytes__ long
368 #[derive(Eq, PartialEq, Debug, Clone)]
369 pub struct Description(String);
372 #[derive(Eq, PartialEq, Debug, Clone)]
373 pub struct PayeePubKey(pub PublicKey);
375 /// Positive duration that defines when (relatively to the timestamp) in the future the invoice
379 /// The number of seconds this expiry time represents has to be in the range
380 /// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
382 #[derive(Eq, PartialEq, Debug, Clone)]
383 pub struct ExpiryTime(Duration);
385 /// `min_final_cltv_expiry` to use for the last HTLC in the route
386 #[derive(Eq, PartialEq, Debug, Clone)]
387 pub struct MinFinalCltvExpiry(pub u64);
389 // TODO: better types instead onf byte arrays
390 /// Fallback address in case no LN payment is possible
391 #[allow(missing_docs)]
392 #[derive(Eq, PartialEq, Debug, Clone)]
398 PubKeyHash([u8; 20]),
399 ScriptHash([u8; 20]),
402 /// Recoverable signature
403 #[derive(Eq, PartialEq, Debug, Clone)]
404 pub struct InvoiceSignature(pub RecoverableSignature);
406 /// Private routing information
409 /// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
411 #[derive(Eq, PartialEq, Debug, Clone)]
412 pub struct RouteHint(Vec<RouteHintHop>);
414 /// Tag constants as specified in BOLT11
415 #[allow(missing_docs)]
417 pub const TAG_PAYMENT_HASH: u8 = 1;
418 pub const TAG_DESCRIPTION: u8 = 13;
419 pub const TAG_PAYEE_PUB_KEY: u8 = 19;
420 pub const TAG_DESCRIPTION_HASH: u8 = 23;
421 pub const TAG_EXPIRY_TIME: u8 = 6;
422 pub const TAG_MIN_FINAL_CLTV_EXPIRY: u8 = 24;
423 pub const TAG_FALLBACK: u8 = 9;
424 pub const TAG_ROUTE: u8 = 3;
425 pub const TAG_PAYMENT_SECRET: u8 = 16;
426 pub const TAG_FEATURES: u8 = 5;
429 impl InvoiceBuilder<tb::False, tb::False, tb::False, tb::False> {
430 /// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
431 /// `InvoiceBuilder::build(self)` becomes available.
432 pub fn new(currrency: Currency) -> Self {
438 tagged_fields: Vec::new(),
441 phantom_d: std::marker::PhantomData,
442 phantom_h: std::marker::PhantomData,
443 phantom_t: std::marker::PhantomData,
444 phantom_c: std::marker::PhantomData,
449 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C> {
450 /// Helper function to set the completeness flags.
451 fn set_flags<DN: tb::Bool, HN: tb::Bool, TN: tb::Bool, CN: tb::Bool>(self) -> InvoiceBuilder<DN, HN, TN, CN> {
452 InvoiceBuilder::<DN, HN, TN, CN> {
453 currency: self.currency,
455 si_prefix: self.si_prefix,
456 timestamp: self.timestamp,
457 tagged_fields: self.tagged_fields,
460 phantom_d: std::marker::PhantomData,
461 phantom_h: std::marker::PhantomData,
462 phantom_t: std::marker::PhantomData,
463 phantom_c: std::marker::PhantomData,
467 /// Sets the amount in pico BTC. The optimal SI prefix is choosen automatically.
468 pub fn amount_pico_btc(mut self, amount: u64) -> Self {
469 let biggest_possible_si_prefix = SiPrefix::values_desc()
471 .find(|prefix| amount % prefix.multiplier() == 0)
472 .expect("Pico should always match");
473 self.amount = Some(amount / biggest_possible_si_prefix.multiplier());
474 self.si_prefix = Some(*biggest_possible_si_prefix);
478 /// Sets the payee's public key.
479 pub fn payee_pub_key(mut self, pub_key: PublicKey) -> Self {
480 self.tagged_fields.push(TaggedField::PayeePubKey(PayeePubKey(pub_key)));
484 /// Sets the payment secret
485 pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> Self {
486 self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret));
490 /// Sets the expiry time
491 pub fn expiry_time(mut self, expiry_time: Duration) -> Self {
492 match ExpiryTime::from_duration(expiry_time) {
493 Ok(t) => self.tagged_fields.push(TaggedField::ExpiryTime(t)),
494 Err(e) => self.error = Some(e),
499 /// Adds a fallback address.
500 pub fn fallback(mut self, fallback: Fallback) -> Self {
501 self.tagged_fields.push(TaggedField::Fallback(fallback));
505 /// Adds a private route.
506 pub fn route(mut self, route: Vec<RouteHintHop>) -> Self {
507 match RouteHint::new(route) {
508 Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
509 Err(e) => self.error = Some(e),
514 /// Adds a features field which indicates the set of supported protocol extensions which the
515 /// origin node supports.
516 pub fn features(mut self, features: InvoiceFeatures) -> Self {
517 self.tagged_fields.push(TaggedField::Features(features));
522 impl<D: tb::Bool, H: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, tb::True, C> {
523 /// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields.
524 pub fn build_raw(self) -> Result<RawInvoice, CreationError> {
526 // If an error occurred at any time before, return it now
527 if let Some(e) = self.error {
532 currency: self.currency,
533 raw_amount: self.amount,
534 si_prefix: self.si_prefix,
537 let timestamp = self.timestamp.expect("ensured to be Some(t) by type T");
539 let tagged_fields = self.tagged_fields.into_iter().map(|tf| {
540 RawTaggedField::KnownSemantics(tf)
541 }).collect::<Vec<_>>();
543 let data = RawDataPart {
544 timestamp: timestamp,
545 tagged_fields: tagged_fields,
555 impl<H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<tb::False, H, T, C> {
556 /// Set the description. This function is only available if no description (hash) was set.
557 pub fn description(mut self, description: String) -> InvoiceBuilder<tb::True, H, T, C> {
558 match Description::new(description) {
559 Ok(d) => self.tagged_fields.push(TaggedField::Description(d)),
560 Err(e) => self.error = Some(e),
565 /// Set the description hash. This function is only available if no description (hash) was set.
566 pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder<tb::True, H, T, C> {
567 self.tagged_fields.push(TaggedField::DescriptionHash(Sha256(description_hash)));
572 impl<D: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, tb::False, T, C> {
573 /// Set the payment hash. This function is only available if no payment hash was set.
574 pub fn payment_hash(mut self, hash: sha256::Hash) -> InvoiceBuilder<D, tb::True, T, C> {
575 self.tagged_fields.push(TaggedField::PaymentHash(Sha256(hash)));
580 impl<D: tb::Bool, H: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, tb::False, C> {
581 /// Sets the timestamp.
582 pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder<D, H, tb::True, C> {
583 match PositiveTimestamp::from_system_time(time) {
584 Ok(t) => self.timestamp = Some(t),
585 Err(e) => self.error = Some(e),
591 /// Sets the timestamp to the current UNIX timestamp.
592 pub fn current_timestamp(mut self) -> InvoiceBuilder<D, H, tb::True, C> {
593 let now = PositiveTimestamp::from_system_time(SystemTime::now());
594 self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen"));
599 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T, tb::False> {
600 /// Sets `min_final_cltv_expiry`.
601 pub fn min_final_cltv_expiry(mut self, min_final_cltv_expiry: u64) -> InvoiceBuilder<D, H, T, tb::True> {
602 self.tagged_fields.push(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry(min_final_cltv_expiry)));
607 impl InvoiceBuilder<tb::True, tb::True, tb::True, tb::True> {
608 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
609 /// and MUST produce a recoverable signature valid for the given hash and if applicable also for
610 /// the included payee public key.
611 pub fn build_signed<F>(self, sign_function: F) -> Result<Invoice, CreationError>
612 where F: FnOnce(&Message) -> RecoverableSignature
614 let invoice = self.try_build_signed::<_, ()>(|hash| {
615 Ok(sign_function(hash))
620 Err(SignOrCreationError::CreationError(e)) => Err(e),
621 Err(SignOrCreationError::SignError(())) => unreachable!(),
625 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY fail with
626 /// an error of type `E` and MUST produce a recoverable signature valid for the given hash and
627 /// if applicable also for the included payee public key.
628 pub fn try_build_signed<F, E>(self, sign_function: F) -> Result<Invoice, SignOrCreationError<E>>
629 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
631 let raw = match self.build_raw() {
633 Err(e) => return Err(SignOrCreationError::CreationError(e)),
636 let signed = match raw.sign(sign_function) {
638 Err(e) => return Err(SignOrCreationError::SignError(e)),
641 let invoice = Invoice {
642 signed_invoice: signed,
645 invoice.check_field_counts().expect("should be ensured by type signature of builder");
652 impl SignedRawInvoice {
653 /// Disassembles the `SignedRawInvoice` into its three parts:
655 /// 2. hash of the raw invoice
657 pub fn into_parts(self) -> (RawInvoice, [u8; 32], InvoiceSignature) {
658 (self.raw_invoice, self.hash, self.signature)
661 /// The `RawInvoice` which was signed.
662 pub fn raw_invoice(&self) -> &RawInvoice {
666 /// The hash of the `RawInvoice` that was signed.
667 pub fn hash(&self) -> &[u8; 32] {
671 /// InvoiceSignature for the invoice.
672 pub fn signature(&self) -> &InvoiceSignature {
676 /// Recovers the public key used for signing the invoice from the recoverable signature.
677 pub fn recover_payee_pub_key(&self) -> Result<PayeePubKey, secp256k1::Error> {
678 let hash = Message::from_slice(&self.hash[..])
679 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
681 Ok(PayeePubKey(Secp256k1::new().recover(
687 /// Checks if the signature is valid for the included payee public key or if none exists if it's
688 /// valid for the recovered signature (which should always be true?).
689 pub fn check_signature(&self) -> bool {
690 let included_pub_key = self.raw_invoice.payee_pub_key();
692 let mut recovered_pub_key = Option::None;
693 if recovered_pub_key.is_none() {
694 let recovered = match self.recover_payee_pub_key() {
696 Err(_) => return false,
698 recovered_pub_key = Some(recovered);
701 let pub_key = included_pub_key.or_else(|| recovered_pub_key.as_ref())
702 .expect("One is always present");
704 let hash = Message::from_slice(&self.hash[..])
705 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
707 let secp_context = Secp256k1::new();
708 let verification_result = secp_context.verify(
710 &self.signature.to_standard(),
714 match verification_result {
721 /// Finds the first element of an enum stream of a given variant and extracts one member of the
722 /// variant. If no element was found `None` gets returned.
724 /// The following example would extract the first
733 /// let elements = vec![A(1), A(2), B(3), A(4)]
735 /// assert_eq!(find_extract!(elements.iter(), Enum::B(ref x), x), Some(3u16))
737 macro_rules! find_extract {
738 ($iter:expr, $enm:pat, $enm_var:ident) => {
739 $iter.filter_map(|tf| match *tf {
740 $enm => Some($enm_var),
746 #[allow(missing_docs)]
748 /// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
749 pub(crate) fn construct_invoice_preimage(hrp_bytes: &[u8], data_without_signature: &[u5]) -> Vec<u8> {
750 use bech32::FromBase32;
752 let mut preimage = Vec::<u8>::from(hrp_bytes);
754 let mut data_part = Vec::from(data_without_signature);
755 let overhang = (data_part.len() * 5) % 8;
757 // add padding if data does not end at a byte boundary
758 data_part.push(u5::try_from_u8(0).unwrap());
760 // if overhang is in (1..3) we need to add u5(0) padding two times
762 data_part.push(u5::try_from_u8(0).unwrap());
766 preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
767 .expect("No padding error may occur due to appended zero above."));
771 /// Hash the HRP as bytes and signatureless data part.
772 fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
773 let preimage = RawInvoice::construct_invoice_preimage(hrp_bytes, data_without_signature);
774 let mut hash: [u8; 32] = Default::default();
775 hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
779 /// Calculate the hash of the encoded `RawInvoice`
780 pub fn hash(&self) -> [u8; 32] {
781 use bech32::ToBase32;
783 RawInvoice::hash_from_parts(
784 self.hrp.to_string().as_bytes(),
785 &self.data.to_base32()
789 /// Signs the invoice using the supplied `sign_function`. This function MAY fail with an error
790 /// of type `E`. Since the signature of a `SignedRawInvoice` is not required to be valid there
791 /// are no constraints regarding the validity of the produced signature.
793 /// (C-not exported) As we don't currently support passing function pointers into methods
795 pub fn sign<F, E>(self, sign_method: F) -> Result<SignedRawInvoice, E>
796 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
798 let raw_hash = self.hash();
799 let hash = Message::from_slice(&raw_hash[..])
800 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
801 let signature = sign_method(&hash)?;
803 Ok(SignedRawInvoice {
806 signature: InvoiceSignature(signature),
810 /// Returns an iterator over all tagged fields with known semantics.
812 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
813 pub fn known_tagged_fields(&self)
814 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>>
816 // For 1.14.0 compatibility: closures' types can't be written an fn()->() in the
817 // function's type signature.
818 // TODO: refactor once impl Trait is available
819 fn match_raw(raw: &RawTaggedField) -> Option<&TaggedField> {
821 RawTaggedField::KnownSemantics(ref tf) => Some(tf),
826 self.data.tagged_fields.iter().filter_map(match_raw )
829 pub fn payment_hash(&self) -> Option<&Sha256> {
830 find_extract!(self.known_tagged_fields(), TaggedField::PaymentHash(ref x), x)
833 pub fn description(&self) -> Option<&Description> {
834 find_extract!(self.known_tagged_fields(), TaggedField::Description(ref x), x)
837 pub fn payee_pub_key(&self) -> Option<&PayeePubKey> {
838 find_extract!(self.known_tagged_fields(), TaggedField::PayeePubKey(ref x), x)
841 pub fn description_hash(&self) -> Option<&Sha256> {
842 find_extract!(self.known_tagged_fields(), TaggedField::DescriptionHash(ref x), x)
845 pub fn expiry_time(&self) -> Option<&ExpiryTime> {
846 find_extract!(self.known_tagged_fields(), TaggedField::ExpiryTime(ref x), x)
849 pub fn min_final_cltv_expiry(&self) -> Option<&MinFinalCltvExpiry> {
850 find_extract!(self.known_tagged_fields(), TaggedField::MinFinalCltvExpiry(ref x), x)
853 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
854 find_extract!(self.known_tagged_fields(), TaggedField::PaymentSecret(ref x), x)
857 pub fn features(&self) -> Option<&InvoiceFeatures> {
858 find_extract!(self.known_tagged_fields(), TaggedField::Features(ref x), x)
861 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
862 pub fn fallbacks(&self) -> Vec<&Fallback> {
863 self.known_tagged_fields().filter_map(|tf| match tf {
864 &TaggedField::Fallback(ref f) => Some(f),
866 }).collect::<Vec<&Fallback>>()
869 pub fn routes(&self) -> Vec<&RouteHint> {
870 self.known_tagged_fields().filter_map(|tf| match tf {
871 &TaggedField::Route(ref r) => Some(r),
873 }).collect::<Vec<&RouteHint>>()
876 pub fn amount_pico_btc(&self) -> Option<u64> {
877 self.hrp.raw_amount.map(|v| {
878 v * self.hrp.si_prefix.as_ref().map_or(1_000_000_000_000, |si| { si.multiplier() })
882 pub fn currency(&self) -> Currency {
883 self.hrp.currency.clone()
887 impl PositiveTimestamp {
888 /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
889 /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
890 /// `CreationError::TimestampOutOfBounds`.
891 pub fn from_unix_timestamp(unix_seconds: u64) -> Result<Self, CreationError> {
892 if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
893 Err(CreationError::TimestampOutOfBounds)
895 Ok(PositiveTimestamp(UNIX_EPOCH + Duration::from_secs(unix_seconds)))
899 /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
900 /// the Range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
901 /// `CreationError::TimestampOutOfBounds`.
902 pub fn from_system_time(time: SystemTime) -> Result<Self, CreationError> {
904 .duration_since(UNIX_EPOCH)
905 .map(|t| t.as_secs() <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)
908 Ok(PositiveTimestamp(time))
910 Err(CreationError::TimestampOutOfBounds)
914 /// Returns the UNIX timestamp representing the stored time
915 pub fn as_unix_timestamp(&self) -> u64 {
916 self.0.duration_since(UNIX_EPOCH)
917 .expect("ensured by type contract/constructors")
921 /// Returns a reference to the internal `SystemTime` time representation
922 pub fn as_time(&self) -> &SystemTime {
927 impl Into<SystemTime> for PositiveTimestamp {
928 fn into(self) -> SystemTime {
933 impl Deref for PositiveTimestamp {
934 type Target = SystemTime;
936 fn deref(&self) -> &Self::Target {
942 /// Transform the `Invoice` into it's unchecked version
943 pub fn into_signed_raw(self) -> SignedRawInvoice {
947 /// Check that all mandatory fields are present
948 fn check_field_counts(&self) -> Result<(), SemanticError> {
949 // "A writer MUST include exactly one p field […]."
950 let payment_hash_cnt = self.tagged_fields().filter(|&tf| match *tf {
951 TaggedField::PaymentHash(_) => true,
954 if payment_hash_cnt < 1 {
955 return Err(SemanticError::NoPaymentHash);
956 } else if payment_hash_cnt > 1 {
957 return Err(SemanticError::MultiplePaymentHashes);
960 // "A writer MUST include either exactly one d or exactly one h field."
961 let description_cnt = self.tagged_fields().filter(|&tf| match *tf {
962 TaggedField::Description(_) | TaggedField::DescriptionHash(_) => true,
965 if description_cnt < 1 {
966 return Err(SemanticError::NoDescription);
967 } else if description_cnt > 1 {
968 return Err(SemanticError::MultipleDescriptions);
974 /// Check that the invoice is signed correctly and that key recovery works
975 pub fn check_signature(&self) -> Result<(), SemanticError> {
976 match self.signed_invoice.recover_payee_pub_key() {
977 Err(secp256k1::Error::InvalidRecoveryId) =>
978 return Err(SemanticError::InvalidRecoveryId),
979 Err(_) => panic!("no other error may occur"),
983 if !self.signed_invoice.check_signature() {
984 return Err(SemanticError::InvalidSignature);
990 /// Constructs an `Invoice` from a `SignedInvoice` by checking all its invariants.
992 /// use lightning_invoice::*;
994 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
995 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
996 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
999 /// let signed = invoice.parse::<SignedRawInvoice>().unwrap();
1001 /// assert!(Invoice::from_signed(signed).is_ok());
1003 pub fn from_signed(signed_invoice: SignedRawInvoice) -> Result<Self, SemanticError> {
1004 let invoice = Invoice {
1005 signed_invoice: signed_invoice,
1007 invoice.check_field_counts()?;
1008 invoice.check_signature()?;
1013 /// Returns the `Invoice`'s timestamp (should equal it's creation time)
1014 pub fn timestamp(&self) -> &SystemTime {
1015 self.signed_invoice.raw_invoice().data.timestamp.as_time()
1018 /// Returns an iterator over all tagged fields of this Invoice.
1020 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
1021 pub fn tagged_fields(&self)
1022 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>> {
1023 self.signed_invoice.raw_invoice().known_tagged_fields()
1026 /// Returns the hash to which we will receive the preimage on completion of the payment
1027 pub fn payment_hash(&self) -> &sha256::Hash {
1028 &self.signed_invoice.payment_hash().expect("checked by constructor").0
1031 /// Return the description or a hash of it for longer ones
1033 /// (C-not exported) because we don't yet export InvoiceDescription
1034 pub fn description(&self) -> InvoiceDescription {
1035 if let Some(ref direct) = self.signed_invoice.description() {
1036 return InvoiceDescription::Direct(direct);
1037 } else if let Some(ref hash) = self.signed_invoice.description_hash() {
1038 return InvoiceDescription::Hash(hash);
1040 unreachable!("ensured by constructor");
1043 /// Get the payee's public key if one was included in the invoice
1044 pub fn payee_pub_key(&self) -> Option<&PublicKey> {
1045 self.signed_invoice.payee_pub_key().map(|x| &x.0)
1048 /// Get the payment secret if one was included in the invoice
1049 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
1050 self.signed_invoice.payment_secret()
1053 /// Get the invoice features if they were included in the invoice
1054 pub fn features(&self) -> Option<&InvoiceFeatures> {
1055 self.signed_invoice.features()
1058 /// Recover the payee's public key (only to be used if none was included in the invoice)
1059 pub fn recover_payee_pub_key(&self) -> PublicKey {
1060 self.signed_invoice.recover_payee_pub_key().expect("was checked by constructor").0
1063 /// Returns the invoice's expiry time, if present, otherwise [`DEFAULT_EXPIRY_TIME`].
1064 pub fn expiry_time(&self) -> Duration {
1065 self.signed_invoice.expiry_time()
1067 .unwrap_or(Duration::from_secs(DEFAULT_EXPIRY_TIME))
1070 /// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
1071 /// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
1072 pub fn min_final_cltv_expiry(&self) -> u64 {
1073 self.signed_invoice.min_final_cltv_expiry()
1075 .unwrap_or(DEFAULT_MIN_FINAL_CLTV_EXPIRY)
1078 /// Returns a list of all fallback addresses
1080 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
1081 pub fn fallbacks(&self) -> Vec<&Fallback> {
1082 self.signed_invoice.fallbacks()
1085 /// Returns a list of all routes included in the invoice
1086 pub fn routes(&self) -> Vec<&RouteHint> {
1087 self.signed_invoice.routes()
1090 /// Returns the currency for which the invoice was issued
1091 pub fn currency(&self) -> Currency {
1092 self.signed_invoice.currency()
1095 /// Returns the amount if specified in the invoice as pico <currency>.
1096 pub fn amount_pico_btc(&self) -> Option<u64> {
1097 self.signed_invoice.amount_pico_btc()
1101 impl From<TaggedField> for RawTaggedField {
1102 fn from(tf: TaggedField) -> Self {
1103 RawTaggedField::KnownSemantics(tf)
1108 /// Numeric representation of the field's tag
1109 pub fn tag(&self) -> u5 {
1110 let tag = match *self {
1111 TaggedField::PaymentHash(_) => constants::TAG_PAYMENT_HASH,
1112 TaggedField::Description(_) => constants::TAG_DESCRIPTION,
1113 TaggedField::PayeePubKey(_) => constants::TAG_PAYEE_PUB_KEY,
1114 TaggedField::DescriptionHash(_) => constants::TAG_DESCRIPTION_HASH,
1115 TaggedField::ExpiryTime(_) => constants::TAG_EXPIRY_TIME,
1116 TaggedField::MinFinalCltvExpiry(_) => constants::TAG_MIN_FINAL_CLTV_EXPIRY,
1117 TaggedField::Fallback(_) => constants::TAG_FALLBACK,
1118 TaggedField::Route(_) => constants::TAG_ROUTE,
1119 TaggedField::PaymentSecret(_) => constants::TAG_PAYMENT_SECRET,
1120 TaggedField::Features(_) => constants::TAG_FEATURES,
1123 u5::try_from_u8(tag).expect("all tags defined are <32")
1129 /// Creates a new `Description` if `description` is at most 1023 __bytes__ long,
1130 /// returns `CreationError::DescriptionTooLong` otherwise
1132 /// Please note that single characters may use more than one byte due to UTF8 encoding.
1133 pub fn new(description: String) -> Result<Description, CreationError> {
1134 if description.len() > 639 {
1135 Err(CreationError::DescriptionTooLong)
1137 Ok(Description(description))
1141 /// Returns the underlying description `String`
1142 pub fn into_inner(self) -> String {
1147 impl Into<String> for Description {
1148 fn into(self) -> String {
1153 impl Deref for Description {
1156 fn deref(&self) -> &str {
1161 impl From<PublicKey> for PayeePubKey {
1162 fn from(pk: PublicKey) -> Self {
1167 impl Deref for PayeePubKey {
1168 type Target = PublicKey;
1170 fn deref(&self) -> &PublicKey {
1176 /// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1177 /// overflow on adding the `EpiryTime` to it then this function will return a
1178 /// `CreationError::ExpiryTimeOutOfBounds`.
1179 pub fn from_seconds(seconds: u64) -> Result<ExpiryTime, CreationError> {
1180 if seconds <= MAX_EXPIRY_TIME {
1181 Ok(ExpiryTime(Duration::from_secs(seconds)))
1183 Err(CreationError::ExpiryTimeOutOfBounds)
1187 /// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1188 /// would overflow on adding the `EpiryTime` to it then this function will return a
1189 /// `CreationError::ExpiryTimeOutOfBounds`.
1190 pub fn from_duration(duration: Duration) -> Result<ExpiryTime, CreationError> {
1191 if duration.as_secs() <= MAX_EXPIRY_TIME {
1192 Ok(ExpiryTime(duration))
1194 Err(CreationError::ExpiryTimeOutOfBounds)
1198 /// Returns the expiry time in seconds
1199 pub fn as_seconds(&self) -> u64 {
1203 /// Returns a reference to the underlying `Duration` (=expiry time)
1204 pub fn as_duration(&self) -> &Duration {
1210 /// Create a new (partial) route from a list of hops
1211 pub fn new(hops: Vec<RouteHintHop>) -> Result<RouteHint, CreationError> {
1212 if hops.len() <= 12 {
1215 Err(CreationError::RouteTooLong)
1219 /// Returrn the underlying vector of hops
1220 pub fn into_inner(self) -> Vec<RouteHintHop> {
1225 impl Into<Vec<RouteHintHop>> for RouteHint {
1226 fn into(self) -> Vec<RouteHintHop> {
1231 impl Deref for RouteHint {
1232 type Target = Vec<RouteHintHop>;
1234 fn deref(&self) -> &Vec<RouteHintHop> {
1239 impl Deref for InvoiceSignature {
1240 type Target = RecoverableSignature;
1242 fn deref(&self) -> &RecoverableSignature {
1247 impl Deref for SignedRawInvoice {
1248 type Target = RawInvoice;
1250 fn deref(&self) -> &RawInvoice {
1255 /// Errors that may occur when constructing a new `RawInvoice` or `Invoice`
1256 #[derive(Eq, PartialEq, Debug, Clone)]
1257 pub enum CreationError {
1258 /// The supplied description string was longer than 639 __bytes__ (see [`Description::new(…)`](./struct.Description.html#method.new))
1261 /// The specified route has too many hops and can't be encoded
1264 /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1265 TimestampOutOfBounds,
1267 /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1268 ExpiryTimeOutOfBounds,
1271 impl Display for CreationError {
1272 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1274 CreationError::DescriptionTooLong => f.write_str("The supplied description string was longer than 639 bytes"),
1275 CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
1276 CreationError::TimestampOutOfBounds => f.write_str("The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`"),
1277 CreationError::ExpiryTimeOutOfBounds => f.write_str("The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`"),
1282 impl std::error::Error for CreationError { }
1284 /// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
1285 /// requirements sections in BOLT #11
1286 #[derive(Eq, PartialEq, Debug, Clone)]
1287 pub enum SemanticError {
1288 /// The invoice is missing the mandatory payment hash
1291 /// The invoice has multiple payment hashes which isn't allowed
1292 MultiplePaymentHashes,
1294 /// No description or description hash are part of the invoice
1297 /// The invoice contains multiple descriptions and/or description hashes which isn't allowed
1298 MultipleDescriptions,
1300 /// The recovery id doesn't fit the signature/pub key
1303 /// The invoice's signature is invalid
1307 impl Display for SemanticError {
1308 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1310 SemanticError::NoPaymentHash => f.write_str("The invoice is missing the mandatory payment hash"),
1311 SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
1312 SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
1313 SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
1314 SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
1315 SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
1320 impl std::error::Error for SemanticError { }
1322 /// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
1325 /// (C-not exported) As we don't support unbounded generics
1326 #[derive(Eq, PartialEq, Debug, Clone)]
1327 pub enum SignOrCreationError<S> {
1328 /// An error occurred during signing
1331 /// An error occurred while building the transaction
1332 CreationError(CreationError),
1335 impl<S> Display for SignOrCreationError<S> {
1336 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1338 SignOrCreationError::SignError(_) => f.write_str("An error occurred during signing"),
1339 SignOrCreationError::CreationError(err) => err.fmt(f),
1346 use bitcoin_hashes::hex::FromHex;
1347 use bitcoin_hashes::sha256;
1350 fn test_system_time_bounds_assumptions() {
1354 ::PositiveTimestamp::from_unix_timestamp(::SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1),
1355 Err(::CreationError::TimestampOutOfBounds)
1359 ::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
1360 Err(::CreationError::ExpiryTimeOutOfBounds)
1365 fn test_calc_invoice_hash() {
1366 use ::{RawInvoice, RawHrp, RawDataPart, Currency, PositiveTimestamp};
1367 use ::TaggedField::*;
1369 let invoice = RawInvoice {
1371 currency: Currency::Bitcoin,
1376 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1377 tagged_fields: vec![
1378 PaymentHash(::Sha256(sha256::Hash::from_hex(
1379 "0001020304050607080900010203040506070809000102030405060708090102"
1380 ).unwrap())).into(),
1381 Description(::Description::new(
1382 "Please consider supporting this project".to_owned()
1388 let expected_hash = [
1389 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27, 0x7b, 0x1d,
1390 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7, 0x83, 0x5d, 0xb2, 0xec,
1391 0xd5, 0x18, 0xe1, 0xc9
1394 assert_eq!(invoice.hash(), expected_hash)
1398 fn test_check_signature() {
1400 use secp256k1::Secp256k1;
1401 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1402 use secp256k1::key::{SecretKey, PublicKey};
1403 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1406 let invoice = SignedRawInvoice {
1407 raw_invoice: RawInvoice {
1409 currency: Currency::Bitcoin,
1414 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1415 tagged_fields: vec ! [
1416 PaymentHash(Sha256(sha256::Hash::from_hex(
1417 "0001020304050607080900010203040506070809000102030405060708090102"
1418 ).unwrap())).into(),
1421 "Please consider supporting this project".to_owned()
1428 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1429 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1430 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1432 signature: InvoiceSignature(RecoverableSignature::from_compact(
1434 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1435 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1436 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1437 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1438 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1439 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1441 RecoveryId::from_i32(0).unwrap()
1445 assert!(invoice.check_signature());
1447 let private_key = SecretKey::from_slice(
1449 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1450 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1451 0x3b, 0x2d, 0xb7, 0x34
1454 let public_key = PublicKey::from_secret_key(&Secp256k1::new(), &private_key);
1456 assert_eq!(invoice.recover_payee_pub_key(), Ok(::PayeePubKey(public_key)));
1458 let (raw_invoice, _, _) = invoice.into_parts();
1459 let new_signed = raw_invoice.sign::<_, ()>(|hash| {
1460 Ok(Secp256k1::new().sign_recoverable(hash, &private_key))
1463 assert!(new_signed.check_signature());
1467 fn test_builder_amount() {
1470 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1471 .description("Test".into())
1472 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1473 .current_timestamp();
1475 let invoice = builder.clone()
1476 .amount_pico_btc(15000)
1480 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Nano));
1481 assert_eq!(invoice.hrp.raw_amount, Some(15));
1484 let invoice = builder.clone()
1485 .amount_pico_btc(1500)
1489 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Pico));
1490 assert_eq!(invoice.hrp.raw_amount, Some(1500));
1494 fn test_builder_fail() {
1496 use std::iter::FromIterator;
1497 use secp256k1::key::PublicKey;
1499 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1500 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1501 .current_timestamp()
1502 .min_final_cltv_expiry(144);
1504 let too_long_string = String::from_iter(
1505 (0..1024).map(|_| '?')
1508 let long_desc_res = builder.clone()
1509 .description(too_long_string)
1511 assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
1513 let route_hop = RouteHintHop {
1514 src_node_id: PublicKey::from_slice(
1516 0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
1517 0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
1518 0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
1521 short_channel_id: 0,
1524 proportional_millionths: 0,
1526 cltv_expiry_delta: 0,
1527 htlc_minimum_msat: None,
1528 htlc_maximum_msat: None,
1530 let too_long_route = vec![route_hop; 13];
1531 let long_route_res = builder.clone()
1532 .description("Test".into())
1533 .route(too_long_route)
1535 assert_eq!(long_route_res, Err(CreationError::RouteTooLong));
1537 let sign_error_res = builder.clone()
1538 .description("Test".into())
1539 .try_build_signed(|_| {
1540 Err("ImaginaryError")
1542 assert_eq!(sign_error_res, Err(SignOrCreationError::SignError("ImaginaryError")));
1546 fn test_builder_ok() {
1548 use secp256k1::Secp256k1;
1549 use secp256k1::key::{SecretKey, PublicKey};
1550 use std::time::{UNIX_EPOCH, Duration};
1552 let secp_ctx = Secp256k1::new();
1554 let private_key = SecretKey::from_slice(
1556 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1557 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1558 0x3b, 0x2d, 0xb7, 0x34
1561 let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
1565 src_node_id: public_key.clone(),
1566 short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
1569 proportional_millionths: 1,
1571 cltv_expiry_delta: 145,
1572 htlc_minimum_msat: None,
1573 htlc_maximum_msat: None,
1576 src_node_id: public_key.clone(),
1577 short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
1580 proportional_millionths: 2,
1582 cltv_expiry_delta: 146,
1583 htlc_minimum_msat: None,
1584 htlc_maximum_msat: None,
1590 src_node_id: public_key.clone(),
1591 short_channel_id: 0,
1594 proportional_millionths: 3,
1596 cltv_expiry_delta: 147,
1597 htlc_minimum_msat: None,
1598 htlc_maximum_msat: None,
1601 src_node_id: public_key.clone(),
1602 short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
1605 proportional_millionths: 4,
1607 cltv_expiry_delta: 148,
1608 htlc_minimum_msat: None,
1609 htlc_maximum_msat: None,
1613 let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
1614 .amount_pico_btc(123)
1615 .timestamp(UNIX_EPOCH + Duration::from_secs(1234567))
1616 .payee_pub_key(public_key.clone())
1617 .expiry_time(Duration::from_secs(54321))
1618 .min_final_cltv_expiry(144)
1619 .fallback(Fallback::PubKeyHash([0;20]))
1620 .route(route_1.clone())
1621 .route(route_2.clone())
1622 .description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
1623 .payment_hash(sha256::Hash::from_slice(&[21;32][..]).unwrap());
1625 let invoice = builder.clone().build_signed(|hash| {
1626 secp_ctx.sign_recoverable(hash, &private_key)
1629 assert!(invoice.check_signature().is_ok());
1630 assert_eq!(invoice.tagged_fields().count(), 8);
1632 assert_eq!(invoice.amount_pico_btc(), Some(123));
1633 assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
1635 invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),
1638 assert_eq!(invoice.payee_pub_key(), Some(&public_key));
1639 assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
1640 assert_eq!(invoice.min_final_cltv_expiry(), 144);
1641 assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
1642 assert_eq!(invoice.routes(), vec![&RouteHint(route_1), &RouteHint(route_2)]);
1644 invoice.description(),
1645 InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
1647 assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&[21;32][..]).unwrap());
1649 let raw_invoice = builder.build_raw().unwrap();
1650 assert_eq!(raw_invoice, *invoice.into_signed_raw().raw_invoice())
1654 fn test_default_values() {
1656 use secp256k1::Secp256k1;
1657 use secp256k1::key::SecretKey;
1659 let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
1660 .description("Test".into())
1661 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1662 .current_timestamp()
1665 .sign::<_, ()>(|hash| {
1666 let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
1667 let secp_ctx = Secp256k1::new();
1668 Ok(secp_ctx.sign_recoverable(hash, &privkey))
1671 let invoice = Invoice::from_signed(signed_invoice).unwrap();
1673 assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
1674 assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));