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
19 extern crate bitcoin_hashes;
20 extern crate lightning;
21 extern crate num_traits;
22 extern crate secp256k1;
25 use bitcoin_hashes::Hash;
26 use bitcoin_hashes::sha256;
27 use lightning::ln::features::InvoiceFeatures;
28 #[cfg(any(doc, test))]
29 use lightning::routing::network_graph::RoutingFees;
30 use lightning::routing::router::RouteHintHop;
32 use secp256k1::key::PublicKey;
33 use secp256k1::{Message, Secp256k1};
34 use secp256k1::recovery::RecoverableSignature;
37 use std::iter::FilterMap;
39 use std::time::{SystemTime, Duration, UNIX_EPOCH};
40 use std::fmt::{Display, Formatter, self};
46 pub use de::{ParseError, ParseOrSemanticError};
48 // TODO: fix before 2037 (see rust PR #55527)
49 /// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
50 /// one of the unit tests, please run them.
51 const SYSTEM_TIME_MAX_UNIX_TIMESTAMP: u64 = std::i32::MAX as u64;
53 /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
54 /// it should be rather low as long as we still have to support 32bit time representations
55 const MAX_EXPIRY_TIME: u64 = 60 * 60 * 24 * 356;
57 /// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
58 /// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
59 /// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
60 /// please open an issue. If all tests pass you should be able to use this library safely by just
61 /// removing this function till we patch it accordingly.
62 fn __system_time_size_check() {
63 // Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
64 // a `Duration` since `SystemTime::UNIX_EPOCH`.
65 unsafe { std::mem::transmute_copy::<SystemTime, [u8; 16]>(&UNIX_EPOCH); }
69 /// **Call this function on startup to ensure that all assumptions about the platform are valid.**
71 /// Unfortunately we have to make assumptions about the upper bounds of the `SystemTime` type on
72 /// your platform which we can't fully verify at compile time and which isn't part of it's contract.
73 /// To our best knowledge our assumptions hold for all platforms officially supported by rust, but
74 /// since this check is fast we recommend to do it anyway.
76 /// If this function fails this is considered a bug. Please open an issue describing your
77 /// platform and stating your current system time.
80 /// If the check fails this function panics. By calling this function on startup you ensure that
81 /// this wont happen at an arbitrary later point in time.
82 pub fn check_platform() {
83 // The upper and lower bounds of `SystemTime` are not part of its public contract and are
84 // platform specific. That's why we have to test if our assumptions regarding these bounds
85 // hold on the target platform.
87 // If this test fails on your platform, please don't use the library and open an issue
88 // instead so we can resolve the situation. Currently this library is tested on:
90 let fail_date = UNIX_EPOCH + Duration::from_secs(SYSTEM_TIME_MAX_UNIX_TIMESTAMP);
91 let year = Duration::from_secs(60 * 60 * 24 * 365);
93 // Make sure that the library will keep working for another year
94 assert!(fail_date.duration_since(SystemTime::now()).unwrap() > year);
96 let max_ts = PositiveTimestamp::from_unix_timestamp(
97 SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
99 let max_exp = ::ExpiryTime::from_seconds(MAX_EXPIRY_TIME).unwrap();
102 (*max_ts.as_time() + *max_exp.as_duration()).duration_since(UNIX_EPOCH).unwrap().as_secs(),
103 SYSTEM_TIME_MAX_UNIX_TIMESTAMP
108 /// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
109 /// that only a semantically and syntactically correct Invoice can be built using it.
112 /// extern crate secp256k1;
113 /// extern crate lightning_invoice;
114 /// extern crate bitcoin_hashes;
116 /// use bitcoin_hashes::Hash;
117 /// use bitcoin_hashes::sha256;
119 /// use secp256k1::Secp256k1;
120 /// use secp256k1::key::SecretKey;
122 /// use lightning_invoice::{Currency, InvoiceBuilder};
125 /// let private_key = SecretKey::from_slice(
127 /// 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f,
128 /// 0xe2, 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04,
129 /// 0xa8, 0xca, 0x3b, 0x2d, 0xb7, 0x34
133 /// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
135 /// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
136 /// .description("Coins pls!".into())
137 /// .payment_hash(payment_hash)
138 /// .current_timestamp()
139 /// .build_signed(|hash| {
140 /// Secp256k1::new().sign_recoverable(hash, &private_key)
144 /// assert!(invoice.to_string().starts_with("lnbc1"));
148 /// # Type parameters
149 /// The two parameters `D` and `H` signal if the builder already contains the correct amount of the
151 /// * `D`: exactly one `Description` or `DescriptionHash`
152 /// * `H`: exactly one `PaymentHash`
153 /// * `T`: the timestamp is set
155 /// (C-not exported) as we likely need to manually select one set of boolean type parameters.
156 #[derive(Eq, PartialEq, Debug, Clone)]
157 pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool> {
160 si_prefix: Option<SiPrefix>,
161 timestamp: Option<PositiveTimestamp>,
162 tagged_fields: Vec<TaggedField>,
163 error: Option<CreationError>,
165 phantom_d: std::marker::PhantomData<D>,
166 phantom_h: std::marker::PhantomData<H>,
167 phantom_t: std::marker::PhantomData<T>,
170 /// Represents a syntactically and semantically correct lightning BOLT11 invoice.
172 /// There are three ways to construct an `Invoice`:
173 /// 1. using `InvoiceBuilder`
174 /// 2. using `Invoice::from_signed(SignedRawInvoice)`
175 /// 3. using `str::parse::<Invoice>(&str)`
176 #[derive(Eq, PartialEq, Debug, Clone)]
178 signed_invoice: SignedRawInvoice,
181 /// Represents the description of an invoice which has to be either a directly included string or
182 /// a hash of a description provided out of band.
184 /// (C-not exported) As we don't have a good way to map the reference lifetimes making this
185 /// practically impossible to use safely in languages like C.
186 #[derive(Eq, PartialEq, Debug, Clone)]
187 pub enum InvoiceDescription<'f> {
188 /// Reference to the directly supplied description in the invoice
189 Direct(&'f Description),
191 /// Reference to the description's hash included in the invoice
195 /// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be
199 /// The hash has to be either from the deserialized invoice or from the serialized `raw_invoice`.
200 #[derive(Eq, PartialEq, Debug, Clone)]
201 pub struct SignedRawInvoice {
202 /// The rawInvoice that the signature belongs to
203 raw_invoice: RawInvoice,
205 /// Hash of the `RawInvoice` that will be used to check the signature.
207 /// * if the `SignedRawInvoice` was deserialized the hash is of from the original encoded form,
208 /// since it's not guaranteed that encoding it again will lead to the same result since integers
209 /// could have been encoded with leading zeroes etc.
210 /// * if the `SignedRawInvoice` was constructed manually the hash will be the calculated hash
211 /// from the `RawInvoice`
214 /// signature of the payment request
215 signature: InvoiceSignature,
218 /// Represents an syntactically correct Invoice for a payment on the lightning network,
219 /// but without the signature information.
220 /// De- and encoding should not lead to information loss but may lead to different hashes.
222 /// For methods without docs see the corresponding methods in `Invoice`.
223 #[derive(Eq, PartialEq, Debug, Clone)]
224 pub struct RawInvoice {
225 /// human readable part
229 pub data: RawDataPart,
232 /// Data of the `RawInvoice` that is encoded in the human readable part
234 /// (C-not exported) As we don't yet support Option<Enum>
235 #[derive(Eq, PartialEq, Debug, Clone)]
237 /// The currency deferred from the 3rd and 4th character of the bech32 transaction
238 pub currency: Currency,
240 /// The amount that, multiplied by the SI prefix, has to be payed
241 pub raw_amount: Option<u64>,
243 /// SI prefix that gets multiplied with the `raw_amount`
244 pub si_prefix: Option<SiPrefix>,
247 /// Data of the `RawInvoice` that is encoded in the data part
248 #[derive(Eq, PartialEq, Debug, Clone)]
249 pub struct RawDataPart {
250 /// generation time of the invoice
251 pub timestamp: PositiveTimestamp,
253 /// tagged fields of the payment request
254 pub tagged_fields: Vec<RawTaggedField>,
257 /// A timestamp that refers to a date after 1 January 1970 which means its representation as UNIX
258 /// timestamp is positive.
261 /// The UNIX timestamp representing the stored time has to be positive and small enough so that
262 /// a `EpiryTime` can be added to it without an overflow.
263 #[derive(Eq, PartialEq, Debug, Clone)]
264 pub struct PositiveTimestamp(SystemTime);
266 /// SI prefixes for the human readable part
267 #[derive(Eq, PartialEq, Debug, Clone, Copy)]
280 /// Returns the multiplier to go from a BTC value to picoBTC implied by this SiPrefix.
281 /// This is effectively 10^12 * the prefix multiplier
282 pub fn multiplier(&self) -> u64 {
284 SiPrefix::Milli => 1_000_000_000,
285 SiPrefix::Micro => 1_000_000,
286 SiPrefix::Nano => 1_000,
291 /// Returns all enum variants of `SiPrefix` sorted in descending order of their associated
294 /// (C-not exported) As we don't yet support a slice of enums, and also because this function
295 /// isn't the most critical to expose.
296 pub fn values_desc() -> &'static [SiPrefix] {
298 static VALUES: [SiPrefix; 4] = [Milli, Micro, Nano, Pico];
303 /// Enum representing the crypto currencies (or networks) supported by this library
304 #[derive(Eq, PartialEq, Debug, Clone)]
315 /// Bitcoin simnet/signet
319 /// Tagged field which may have an unknown tag
320 #[derive(Eq, PartialEq, Debug, Clone)]
321 pub enum RawTaggedField {
322 /// Parsed tagged field with known tag
323 KnownSemantics(TaggedField),
324 /// tagged field which was not parsed due to an unknown tag or undefined field semantics
325 UnknownSemantics(Vec<u5>),
328 /// Tagged field with known tag
330 /// For descriptions of the enum values please refer to the enclosed type's docs.
331 #[allow(missing_docs)]
332 #[derive(Eq, PartialEq, Debug, Clone)]
333 pub enum TaggedField {
335 Description(Description),
336 PayeePubKey(PayeePubKey),
337 DescriptionHash(Sha256),
338 ExpiryTime(ExpiryTime),
339 MinFinalCltvExpiry(MinFinalCltvExpiry),
342 PaymentSecret(PaymentSecret),
343 Features(InvoiceFeatures),
347 #[derive(Eq, PartialEq, Debug, Clone)]
348 pub struct Sha256(pub sha256::Hash);
350 /// Description string
353 /// The description can be at most 639 __bytes__ long
354 #[derive(Eq, PartialEq, Debug, Clone)]
355 pub struct Description(String);
358 #[derive(Eq, PartialEq, Debug, Clone)]
359 pub struct PayeePubKey(pub PublicKey);
361 /// 256-bit payment secret
362 #[derive(Eq, PartialEq, Debug, Clone)]
363 pub struct PaymentSecret(pub [u8; 32]);
365 /// Positive duration that defines when (relatively to the timestamp) in the future the invoice
369 /// The number of seconds this expiry time represents has to be in the range
370 /// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
372 #[derive(Eq, PartialEq, Debug, Clone)]
373 pub struct ExpiryTime(Duration);
375 /// `min_final_cltv_expiry` to use for the last HTLC in the route
376 #[derive(Eq, PartialEq, Debug, Clone)]
377 pub struct MinFinalCltvExpiry(pub u64);
379 // TODO: better types instead onf byte arrays
380 /// Fallback address in case no LN payment is possible
381 #[allow(missing_docs)]
382 #[derive(Eq, PartialEq, Debug, Clone)]
388 PubKeyHash([u8; 20]),
389 ScriptHash([u8; 20]),
392 /// Recoverable signature
393 #[derive(Eq, PartialEq, Debug, Clone)]
394 pub struct InvoiceSignature(pub RecoverableSignature);
396 /// Private routing information
399 /// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
401 #[derive(Eq, PartialEq, Debug, Clone)]
402 pub struct RouteHint(Vec<RouteHintHop>);
404 /// Tag constants as specified in BOLT11
405 #[allow(missing_docs)]
407 pub const TAG_PAYMENT_HASH: u8 = 1;
408 pub const TAG_DESCRIPTION: u8 = 13;
409 pub const TAG_PAYEE_PUB_KEY: u8 = 19;
410 pub const TAG_DESCRIPTION_HASH: u8 = 23;
411 pub const TAG_EXPIRY_TIME: u8 = 6;
412 pub const TAG_MIN_FINAL_CLTV_EXPIRY: u8 = 24;
413 pub const TAG_FALLBACK: u8 = 9;
414 pub const TAG_ROUTE: u8 = 3;
415 pub const TAG_PAYMENT_SECRET: u8 = 16;
416 pub const TAG_FEATURES: u8 = 5;
419 impl InvoiceBuilder<tb::False, tb::False, tb::False> {
420 /// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
421 /// `InvoiceBuilder::build(self)` becomes available.
422 pub fn new(currrency: Currency) -> Self {
428 tagged_fields: Vec::new(),
431 phantom_d: std::marker::PhantomData,
432 phantom_h: std::marker::PhantomData,
433 phantom_t: std::marker::PhantomData,
438 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T> {
439 /// Helper function to set the completeness flags.
440 fn set_flags<DN: tb::Bool, HN: tb::Bool, TN: tb::Bool>(self) -> InvoiceBuilder<DN, HN, TN> {
441 InvoiceBuilder::<DN, HN, TN> {
442 currency: self.currency,
444 si_prefix: self.si_prefix,
445 timestamp: self.timestamp,
446 tagged_fields: self.tagged_fields,
449 phantom_d: std::marker::PhantomData,
450 phantom_h: std::marker::PhantomData,
451 phantom_t: std::marker::PhantomData,
455 /// Sets the amount in pico BTC. The optimal SI prefix is choosen automatically.
456 pub fn amount_pico_btc(mut self, amount: u64) -> Self {
457 let biggest_possible_si_prefix = SiPrefix::values_desc()
459 .find(|prefix| amount % prefix.multiplier() == 0)
460 .expect("Pico should always match");
461 self.amount = Some(amount / biggest_possible_si_prefix.multiplier());
462 self.si_prefix = Some(*biggest_possible_si_prefix);
466 /// Sets the payee's public key.
467 pub fn payee_pub_key(mut self, pub_key: PublicKey) -> Self {
468 self.tagged_fields.push(TaggedField::PayeePubKey(PayeePubKey(pub_key)));
472 /// Sets the payment secret
473 pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> Self {
474 self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret));
478 /// Sets the expiry time
479 pub fn expiry_time(mut self, expiry_time: Duration) -> Self {
480 match ExpiryTime::from_duration(expiry_time) {
481 Ok(t) => self.tagged_fields.push(TaggedField::ExpiryTime(t)),
482 Err(e) => self.error = Some(e),
487 /// Sets `min_final_cltv_expiry`.
488 pub fn min_final_cltv_expiry(mut self, min_final_cltv_expiry: u64) -> Self {
489 self.tagged_fields.push(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry(min_final_cltv_expiry)));
493 /// Adds a fallback address.
494 pub fn fallback(mut self, fallback: Fallback) -> Self {
495 self.tagged_fields.push(TaggedField::Fallback(fallback));
499 /// Adds a private route.
500 pub fn route(mut self, route: Vec<RouteHintHop>) -> Self {
501 match RouteHint::new(route) {
502 Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
503 Err(e) => self.error = Some(e),
508 /// Adds a features field which indicates the set of supported protocol extensions which the
509 /// origin node supports.
510 pub fn features(mut self, features: InvoiceFeatures) -> Self {
511 self.tagged_fields.push(TaggedField::Features(features));
516 impl<D: tb::Bool, H: tb::Bool> InvoiceBuilder<D, H, tb::True> {
517 /// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields.
518 pub fn build_raw(self) -> Result<RawInvoice, CreationError> {
520 // If an error occurred at any time before, return it now
521 if let Some(e) = self.error {
526 currency: self.currency,
527 raw_amount: self.amount,
528 si_prefix: self.si_prefix,
531 let timestamp = self.timestamp.expect("ensured to be Some(t) by type T");
533 let tagged_fields = self.tagged_fields.into_iter().map(|tf| {
534 RawTaggedField::KnownSemantics(tf)
535 }).collect::<Vec<_>>();
537 let data = RawDataPart {
538 timestamp: timestamp,
539 tagged_fields: tagged_fields,
549 impl<H: tb::Bool, T: tb::Bool> InvoiceBuilder<tb::False, H, T> {
550 /// Set the description. This function is only available if no description (hash) was set.
551 pub fn description(mut self, description: String) -> InvoiceBuilder<tb::True, H, T> {
552 match Description::new(description) {
553 Ok(d) => self.tagged_fields.push(TaggedField::Description(d)),
554 Err(e) => self.error = Some(e),
559 /// Set the description hash. This function is only available if no description (hash) was set.
560 pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder<tb::True, H, T> {
561 self.tagged_fields.push(TaggedField::DescriptionHash(Sha256(description_hash)));
566 impl<D: tb::Bool, T: tb::Bool> InvoiceBuilder<D, tb::False, T> {
567 /// Set the payment hash. This function is only available if no payment hash was set.
568 pub fn payment_hash(mut self, hash: sha256::Hash) -> InvoiceBuilder<D, tb::True, T> {
569 self.tagged_fields.push(TaggedField::PaymentHash(Sha256(hash)));
574 impl<D: tb::Bool, H: tb::Bool> InvoiceBuilder<D, H, tb::False> {
575 /// Sets the timestamp.
576 pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder<D, H, tb::True> {
577 match PositiveTimestamp::from_system_time(time) {
578 Ok(t) => self.timestamp = Some(t),
579 Err(e) => self.error = Some(e),
585 /// Sets the timestamp to the current UNIX timestamp.
586 pub fn current_timestamp(mut self) -> InvoiceBuilder<D, H, tb::True> {
587 let now = PositiveTimestamp::from_system_time(SystemTime::now());
588 self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen"));
593 impl InvoiceBuilder<tb::True, tb::True, tb::True> {
594 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
595 /// and MUST produce a recoverable signature valid for the given hash and if applicable also for
596 /// the included payee public key.
597 pub fn build_signed<F>(self, sign_function: F) -> Result<Invoice, CreationError>
598 where F: FnOnce(&Message) -> RecoverableSignature
600 let invoice = self.try_build_signed::<_, ()>(|hash| {
601 Ok(sign_function(hash))
606 Err(SignOrCreationError::CreationError(e)) => Err(e),
607 Err(SignOrCreationError::SignError(())) => unreachable!(),
611 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY fail with
612 /// an error of type `E` and MUST produce a recoverable signature valid for the given hash and
613 /// if applicable also for the included payee public key.
614 pub fn try_build_signed<F, E>(self, sign_function: F) -> Result<Invoice, SignOrCreationError<E>>
615 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
617 let raw = match self.build_raw() {
619 Err(e) => return Err(SignOrCreationError::CreationError(e)),
622 let signed = match raw.sign(sign_function) {
624 Err(e) => return Err(SignOrCreationError::SignError(e)),
627 let invoice = Invoice {
628 signed_invoice: signed,
631 invoice.check_field_counts().expect("should be ensured by type signature of builder");
638 impl SignedRawInvoice {
639 /// Disassembles the `SignedRawInvoice` into its three parts:
641 /// 2. hash of the raw invoice
643 pub fn into_parts(self) -> (RawInvoice, [u8; 32], InvoiceSignature) {
644 (self.raw_invoice, self.hash, self.signature)
647 /// The `RawInvoice` which was signed.
648 pub fn raw_invoice(&self) -> &RawInvoice {
652 /// The hash of the `RawInvoice` that was signed.
653 pub fn hash(&self) -> &[u8; 32] {
657 /// InvoiceSignature for the invoice.
658 pub fn signature(&self) -> &InvoiceSignature {
662 /// Recovers the public key used for signing the invoice from the recoverable signature.
663 pub fn recover_payee_pub_key(&self) -> Result<PayeePubKey, secp256k1::Error> {
664 let hash = Message::from_slice(&self.hash[..])
665 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
667 Ok(PayeePubKey(Secp256k1::new().recover(
673 /// Checks if the signature is valid for the included payee public key or if none exists if it's
674 /// valid for the recovered signature (which should always be true?).
675 pub fn check_signature(&self) -> bool {
676 let included_pub_key = self.raw_invoice.payee_pub_key();
678 let mut recovered_pub_key = Option::None;
679 if recovered_pub_key.is_none() {
680 let recovered = match self.recover_payee_pub_key() {
682 Err(_) => return false,
684 recovered_pub_key = Some(recovered);
687 let pub_key = included_pub_key.or_else(|| recovered_pub_key.as_ref())
688 .expect("One is always present");
690 let hash = Message::from_slice(&self.hash[..])
691 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
693 let secp_context = Secp256k1::new();
694 let verification_result = secp_context.verify(
696 &self.signature.to_standard(),
700 match verification_result {
707 /// Finds the first element of an enum stream of a given variant and extracts one member of the
708 /// variant. If no element was found `None` gets returned.
710 /// The following example would extract the first
719 /// let elements = vec![A(1), A(2), B(3), A(4)]
721 /// assert_eq!(find_extract!(elements.iter(), Enum::B(ref x), x), Some(3u16))
723 macro_rules! find_extract {
724 ($iter:expr, $enm:pat, $enm_var:ident) => {
725 $iter.filter_map(|tf| match *tf {
726 $enm => Some($enm_var),
732 #[allow(missing_docs)]
734 /// Hash the HRP as bytes and signatureless data part.
735 fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
736 use bech32::FromBase32;
738 let mut preimage = Vec::<u8>::from(hrp_bytes);
740 let mut data_part = Vec::from(data_without_signature);
741 let overhang = (data_part.len() * 5) % 8;
743 // add padding if data does not end at a byte boundary
744 data_part.push(u5::try_from_u8(0).unwrap());
746 // if overhang is in (1..3) we need to add u5(0) padding two times
748 data_part.push(u5::try_from_u8(0).unwrap());
752 preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
753 .expect("No padding error may occur due to appended zero above."));
755 let mut hash: [u8; 32] = Default::default();
756 hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
760 /// Calculate the hash of the encoded `RawInvoice`
761 pub fn hash(&self) -> [u8; 32] {
762 use bech32::ToBase32;
764 RawInvoice::hash_from_parts(
765 self.hrp.to_string().as_bytes(),
766 &self.data.to_base32()
770 /// Signs the invoice using the supplied `sign_function`. This function MAY fail with an error
771 /// of type `E`. Since the signature of a `SignedRawInvoice` is not required to be valid there
772 /// are no constraints regarding the validity of the produced signature.
774 /// (C-not exported) As we don't currently support passing function pointers into methods
776 pub fn sign<F, E>(self, sign_method: F) -> Result<SignedRawInvoice, E>
777 where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
779 let raw_hash = self.hash();
780 let hash = Message::from_slice(&raw_hash[..])
781 .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
782 let signature = sign_method(&hash)?;
784 Ok(SignedRawInvoice {
787 signature: InvoiceSignature(signature),
791 /// Returns an iterator over all tagged fields with known semantics.
793 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
794 pub fn known_tagged_fields(&self)
795 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>>
797 // For 1.14.0 compatibility: closures' types can't be written an fn()->() in the
798 // function's type signature.
799 // TODO: refactor once impl Trait is available
800 fn match_raw(raw: &RawTaggedField) -> Option<&TaggedField> {
802 RawTaggedField::KnownSemantics(ref tf) => Some(tf),
807 self.data.tagged_fields.iter().filter_map(match_raw )
810 pub fn payment_hash(&self) -> Option<&Sha256> {
811 find_extract!(self.known_tagged_fields(), TaggedField::PaymentHash(ref x), x)
814 pub fn description(&self) -> Option<&Description> {
815 find_extract!(self.known_tagged_fields(), TaggedField::Description(ref x), x)
818 pub fn payee_pub_key(&self) -> Option<&PayeePubKey> {
819 find_extract!(self.known_tagged_fields(), TaggedField::PayeePubKey(ref x), x)
822 pub fn description_hash(&self) -> Option<&Sha256> {
823 find_extract!(self.known_tagged_fields(), TaggedField::DescriptionHash(ref x), x)
826 pub fn expiry_time(&self) -> Option<&ExpiryTime> {
827 find_extract!(self.known_tagged_fields(), TaggedField::ExpiryTime(ref x), x)
830 pub fn min_final_cltv_expiry(&self) -> Option<&MinFinalCltvExpiry> {
831 find_extract!(self.known_tagged_fields(), TaggedField::MinFinalCltvExpiry(ref x), x)
834 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
835 find_extract!(self.known_tagged_fields(), TaggedField::PaymentSecret(ref x), x)
838 pub fn features(&self) -> Option<&InvoiceFeatures> {
839 find_extract!(self.known_tagged_fields(), TaggedField::Features(ref x), x)
842 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
843 pub fn fallbacks(&self) -> Vec<&Fallback> {
844 self.known_tagged_fields().filter_map(|tf| match tf {
845 &TaggedField::Fallback(ref f) => Some(f),
847 }).collect::<Vec<&Fallback>>()
850 pub fn routes(&self) -> Vec<&RouteHint> {
851 self.known_tagged_fields().filter_map(|tf| match tf {
852 &TaggedField::Route(ref r) => Some(r),
854 }).collect::<Vec<&RouteHint>>()
857 pub fn amount_pico_btc(&self) -> Option<u64> {
858 self.hrp.raw_amount.map(|v| {
859 v * self.hrp.si_prefix.as_ref().map_or(1_000_000_000_000, |si| { si.multiplier() })
863 pub fn currency(&self) -> Currency {
864 self.hrp.currency.clone()
868 impl PositiveTimestamp {
869 /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
870 /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
871 /// `CreationError::TimestampOutOfBounds`.
872 pub fn from_unix_timestamp(unix_seconds: u64) -> Result<Self, CreationError> {
873 if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
874 Err(CreationError::TimestampOutOfBounds)
876 Ok(PositiveTimestamp(UNIX_EPOCH + Duration::from_secs(unix_seconds)))
880 /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
881 /// the Range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
882 /// `CreationError::TimestampOutOfBounds`.
883 pub fn from_system_time(time: SystemTime) -> Result<Self, CreationError> {
885 .duration_since(UNIX_EPOCH)
886 .map(|t| t.as_secs() <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)
889 Ok(PositiveTimestamp(time))
891 Err(CreationError::TimestampOutOfBounds)
895 /// Returns the UNIX timestamp representing the stored time
896 pub fn as_unix_timestamp(&self) -> u64 {
897 self.0.duration_since(UNIX_EPOCH)
898 .expect("ensured by type contract/constructors")
902 /// Returns a reference to the internal `SystemTime` time representation
903 pub fn as_time(&self) -> &SystemTime {
908 impl Into<SystemTime> for PositiveTimestamp {
909 fn into(self) -> SystemTime {
914 impl Deref for PositiveTimestamp {
915 type Target = SystemTime;
917 fn deref(&self) -> &Self::Target {
923 /// Transform the `Invoice` into it's unchecked version
924 pub fn into_signed_raw(self) -> SignedRawInvoice {
928 /// Check that all mandatory fields are present
929 fn check_field_counts(&self) -> Result<(), SemanticError> {
930 // "A writer MUST include exactly one p field […]."
931 let payment_hash_cnt = self.tagged_fields().filter(|&tf| match *tf {
932 TaggedField::PaymentHash(_) => true,
935 if payment_hash_cnt < 1 {
936 return Err(SemanticError::NoPaymentHash);
937 } else if payment_hash_cnt > 1 {
938 return Err(SemanticError::MultiplePaymentHashes);
941 // "A writer MUST include either exactly one d or exactly one h field."
942 let description_cnt = self.tagged_fields().filter(|&tf| match *tf {
943 TaggedField::Description(_) | TaggedField::DescriptionHash(_) => true,
946 if description_cnt < 1 {
947 return Err(SemanticError::NoDescription);
948 } else if description_cnt > 1 {
949 return Err(SemanticError::MultipleDescriptions);
955 /// Check that the invoice is signed correctly and that key recovery works
956 pub fn check_signature(&self) -> Result<(), SemanticError> {
957 match self.signed_invoice.recover_payee_pub_key() {
958 Err(secp256k1::Error::InvalidRecoveryId) =>
959 return Err(SemanticError::InvalidRecoveryId),
960 Err(_) => panic!("no other error may occur"),
964 if !self.signed_invoice.check_signature() {
965 return Err(SemanticError::InvalidSignature);
971 /// Constructs an `Invoice` from a `SignedInvoice` by checking all its invariants.
973 /// use lightning_invoice::*;
975 /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
976 /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
977 /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
980 /// let signed = invoice.parse::<SignedRawInvoice>().unwrap();
982 /// assert!(Invoice::from_signed(signed).is_ok());
984 pub fn from_signed(signed_invoice: SignedRawInvoice) -> Result<Self, SemanticError> {
985 let invoice = Invoice {
986 signed_invoice: signed_invoice,
988 invoice.check_field_counts()?;
989 invoice.check_signature()?;
994 /// Returns the `Invoice`'s timestamp (should equal it's creation time)
995 pub fn timestamp(&self) -> &SystemTime {
996 self.signed_invoice.raw_invoice().data.timestamp.as_time()
999 /// Returns an iterator over all tagged fields of this Invoice.
1001 /// (C-not exported) As there is not yet a manual mapping for a FilterMap
1002 pub fn tagged_fields(&self)
1003 -> FilterMap<Iter<RawTaggedField>, fn(&RawTaggedField) -> Option<&TaggedField>> {
1004 self.signed_invoice.raw_invoice().known_tagged_fields()
1007 /// Returns the hash to which we will receive the preimage on completion of the payment
1008 pub fn payment_hash(&self) -> &sha256::Hash {
1009 &self.signed_invoice.payment_hash().expect("checked by constructor").0
1012 /// Return the description or a hash of it for longer ones
1014 /// (C-not exported) because we don't yet export InvoiceDescription
1015 pub fn description(&self) -> InvoiceDescription {
1016 if let Some(ref direct) = self.signed_invoice.description() {
1017 return InvoiceDescription::Direct(direct);
1018 } else if let Some(ref hash) = self.signed_invoice.description_hash() {
1019 return InvoiceDescription::Hash(hash);
1021 unreachable!("ensured by constructor");
1024 /// Get the payee's public key if one was included in the invoice
1025 pub fn payee_pub_key(&self) -> Option<&PublicKey> {
1026 self.signed_invoice.payee_pub_key().map(|x| &x.0)
1029 /// Get the payment secret if one was included in the invoice
1030 pub fn payment_secret(&self) -> Option<&PaymentSecret> {
1031 self.signed_invoice.payment_secret()
1034 /// Get the invoice features if they were included in the invoice
1035 pub fn features(&self) -> Option<&InvoiceFeatures> {
1036 self.signed_invoice.features()
1039 /// Recover the payee's public key (only to be used if none was included in the invoice)
1040 pub fn recover_payee_pub_key(&self) -> PublicKey {
1041 self.signed_invoice.recover_payee_pub_key().expect("was checked by constructor").0
1044 /// Returns the invoice's expiry time if present
1045 pub fn expiry_time(&self) -> Duration {
1046 self.signed_invoice.expiry_time()
1048 .unwrap_or(Duration::from_secs(3600))
1051 /// Returns the invoice's `min_cltv_expiry` time if present
1052 pub fn min_final_cltv_expiry(&self) -> Option<u64> {
1053 self.signed_invoice.min_final_cltv_expiry().map(|x| x.0)
1056 /// Returns a list of all fallback addresses
1058 /// (C-not exported) as we don't support Vec<&NonOpaqueType>
1059 pub fn fallbacks(&self) -> Vec<&Fallback> {
1060 self.signed_invoice.fallbacks()
1063 /// Returns a list of all routes included in the invoice
1064 pub fn routes(&self) -> Vec<&RouteHint> {
1065 self.signed_invoice.routes()
1068 /// Returns the currency for which the invoice was issued
1069 pub fn currency(&self) -> Currency {
1070 self.signed_invoice.currency()
1073 /// Returns the amount if specified in the invoice as pico <currency>.
1074 pub fn amount_pico_btc(&self) -> Option<u64> {
1075 self.signed_invoice.amount_pico_btc()
1079 impl From<TaggedField> for RawTaggedField {
1080 fn from(tf: TaggedField) -> Self {
1081 RawTaggedField::KnownSemantics(tf)
1086 /// Numeric representation of the field's tag
1087 pub fn tag(&self) -> u5 {
1088 let tag = match *self {
1089 TaggedField::PaymentHash(_) => constants::TAG_PAYMENT_HASH,
1090 TaggedField::Description(_) => constants::TAG_DESCRIPTION,
1091 TaggedField::PayeePubKey(_) => constants::TAG_PAYEE_PUB_KEY,
1092 TaggedField::DescriptionHash(_) => constants::TAG_DESCRIPTION_HASH,
1093 TaggedField::ExpiryTime(_) => constants::TAG_EXPIRY_TIME,
1094 TaggedField::MinFinalCltvExpiry(_) => constants::TAG_MIN_FINAL_CLTV_EXPIRY,
1095 TaggedField::Fallback(_) => constants::TAG_FALLBACK,
1096 TaggedField::Route(_) => constants::TAG_ROUTE,
1097 TaggedField::PaymentSecret(_) => constants::TAG_PAYMENT_SECRET,
1098 TaggedField::Features(_) => constants::TAG_FEATURES,
1101 u5::try_from_u8(tag).expect("all tags defined are <32")
1107 /// Creates a new `Description` if `description` is at most 1023 __bytes__ long,
1108 /// returns `CreationError::DescriptionTooLong` otherwise
1110 /// Please note that single characters may use more than one byte due to UTF8 encoding.
1111 pub fn new(description: String) -> Result<Description, CreationError> {
1112 if description.len() > 639 {
1113 Err(CreationError::DescriptionTooLong)
1115 Ok(Description(description))
1119 /// Returns the underlying description `String`
1120 pub fn into_inner(self) -> String {
1125 impl Into<String> for Description {
1126 fn into(self) -> String {
1131 impl Deref for Description {
1134 fn deref(&self) -> &str {
1139 impl From<PublicKey> for PayeePubKey {
1140 fn from(pk: PublicKey) -> Self {
1145 impl Deref for PayeePubKey {
1146 type Target = PublicKey;
1148 fn deref(&self) -> &PublicKey {
1154 /// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1155 /// overflow on adding the `EpiryTime` to it then this function will return a
1156 /// `CreationError::ExpiryTimeOutOfBounds`.
1157 pub fn from_seconds(seconds: u64) -> Result<ExpiryTime, CreationError> {
1158 if seconds <= MAX_EXPIRY_TIME {
1159 Ok(ExpiryTime(Duration::from_secs(seconds)))
1161 Err(CreationError::ExpiryTimeOutOfBounds)
1165 /// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1166 /// would overflow on adding the `EpiryTime` to it then this function will return a
1167 /// `CreationError::ExpiryTimeOutOfBounds`.
1168 pub fn from_duration(duration: Duration) -> Result<ExpiryTime, CreationError> {
1169 if duration.as_secs() <= MAX_EXPIRY_TIME {
1170 Ok(ExpiryTime(duration))
1172 Err(CreationError::ExpiryTimeOutOfBounds)
1176 /// Returns the expiry time in seconds
1177 pub fn as_seconds(&self) -> u64 {
1181 /// Returns a reference to the underlying `Duration` (=expiry time)
1182 pub fn as_duration(&self) -> &Duration {
1188 /// Create a new (partial) route from a list of hops
1189 pub fn new(hops: Vec<RouteHintHop>) -> Result<RouteHint, CreationError> {
1190 if hops.len() <= 12 {
1193 Err(CreationError::RouteTooLong)
1197 /// Returrn the underlying vector of hops
1198 pub fn into_inner(self) -> Vec<RouteHintHop> {
1203 impl Into<Vec<RouteHintHop>> for RouteHint {
1204 fn into(self) -> Vec<RouteHintHop> {
1209 impl Deref for RouteHint {
1210 type Target = Vec<RouteHintHop>;
1212 fn deref(&self) -> &Vec<RouteHintHop> {
1217 impl Deref for InvoiceSignature {
1218 type Target = RecoverableSignature;
1220 fn deref(&self) -> &RecoverableSignature {
1225 impl Deref for SignedRawInvoice {
1226 type Target = RawInvoice;
1228 fn deref(&self) -> &RawInvoice {
1233 /// Errors that may occur when constructing a new `RawInvoice` or `Invoice`
1234 #[derive(Eq, PartialEq, Debug, Clone)]
1235 pub enum CreationError {
1236 /// The supplied description string was longer than 639 __bytes__ (see [`Description::new(…)`](./struct.Description.html#method.new))
1239 /// The specified route has too many hops and can't be encoded
1242 /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1243 TimestampOutOfBounds,
1245 /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1246 ExpiryTimeOutOfBounds,
1249 impl Display for CreationError {
1250 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1252 CreationError::DescriptionTooLong => f.write_str("The supplied description string was longer than 639 bytes"),
1253 CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
1254 CreationError::TimestampOutOfBounds => f.write_str("The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`"),
1255 CreationError::ExpiryTimeOutOfBounds => f.write_str("The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`"),
1260 impl std::error::Error for CreationError { }
1262 /// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
1263 /// requirements sections in BOLT #11
1264 #[derive(Eq, PartialEq, Debug, Clone)]
1265 pub enum SemanticError {
1266 /// The invoice is missing the mandatory payment hash
1269 /// The invoice has multiple payment hashes which isn't allowed
1270 MultiplePaymentHashes,
1272 /// No description or description hash are part of the invoice
1275 /// The invoice contains multiple descriptions and/or description hashes which isn't allowed
1276 MultipleDescriptions,
1278 /// The recovery id doesn't fit the signature/pub key
1281 /// The invoice's signature is invalid
1285 impl Display for SemanticError {
1286 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1288 SemanticError::NoPaymentHash => f.write_str("The invoice is missing the mandatory payment hash"),
1289 SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
1290 SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
1291 SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
1292 SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
1293 SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
1298 impl std::error::Error for SemanticError { }
1300 /// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
1303 /// (C-not exported) As we don't support unbounded generics
1304 #[derive(Eq, PartialEq, Debug, Clone)]
1305 pub enum SignOrCreationError<S> {
1306 /// An error occurred during signing
1309 /// An error occurred while building the transaction
1310 CreationError(CreationError),
1313 impl<S> Display for SignOrCreationError<S> {
1314 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1316 SignOrCreationError::SignError(_) => f.write_str("An error occurred during signing"),
1317 SignOrCreationError::CreationError(err) => err.fmt(f),
1324 use bitcoin_hashes::hex::FromHex;
1325 use bitcoin_hashes::sha256;
1328 fn test_system_time_bounds_assumptions() {
1332 ::PositiveTimestamp::from_unix_timestamp(::SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1),
1333 Err(::CreationError::TimestampOutOfBounds)
1337 ::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
1338 Err(::CreationError::ExpiryTimeOutOfBounds)
1343 fn test_calc_invoice_hash() {
1344 use ::{RawInvoice, RawHrp, RawDataPart, Currency, PositiveTimestamp};
1345 use ::TaggedField::*;
1347 let invoice = RawInvoice {
1349 currency: Currency::Bitcoin,
1354 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1355 tagged_fields: vec![
1356 PaymentHash(::Sha256(sha256::Hash::from_hex(
1357 "0001020304050607080900010203040506070809000102030405060708090102"
1358 ).unwrap())).into(),
1359 Description(::Description::new(
1360 "Please consider supporting this project".to_owned()
1366 let expected_hash = [
1367 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27, 0x7b, 0x1d,
1368 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7, 0x83, 0x5d, 0xb2, 0xec,
1369 0xd5, 0x18, 0xe1, 0xc9
1372 assert_eq!(invoice.hash(), expected_hash)
1376 fn test_check_signature() {
1378 use secp256k1::Secp256k1;
1379 use secp256k1::recovery::{RecoveryId, RecoverableSignature};
1380 use secp256k1::key::{SecretKey, PublicKey};
1381 use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
1384 let invoice = SignedRawInvoice {
1385 raw_invoice: RawInvoice {
1387 currency: Currency::Bitcoin,
1392 timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1393 tagged_fields: vec ! [
1394 PaymentHash(Sha256(sha256::Hash::from_hex(
1395 "0001020304050607080900010203040506070809000102030405060708090102"
1396 ).unwrap())).into(),
1399 "Please consider supporting this project".to_owned()
1406 0xc3, 0xd4, 0xe8, 0x3f, 0x64, 0x6f, 0xa7, 0x9a, 0x39, 0x3d, 0x75, 0x27,
1407 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7,
1408 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9
1410 signature: InvoiceSignature(RecoverableSignature::from_compact(
1412 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
1413 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
1414 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
1415 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
1416 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
1417 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
1419 RecoveryId::from_i32(0).unwrap()
1423 assert!(invoice.check_signature());
1425 let private_key = SecretKey::from_slice(
1427 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1428 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1429 0x3b, 0x2d, 0xb7, 0x34
1432 let public_key = PublicKey::from_secret_key(&Secp256k1::new(), &private_key);
1434 assert_eq!(invoice.recover_payee_pub_key(), Ok(::PayeePubKey(public_key)));
1436 let (raw_invoice, _, _) = invoice.into_parts();
1437 let new_signed = raw_invoice.sign::<_, ()>(|hash| {
1438 Ok(Secp256k1::new().sign_recoverable(hash, &private_key))
1441 assert!(new_signed.check_signature());
1445 fn test_builder_amount() {
1448 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1449 .description("Test".into())
1450 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1451 .current_timestamp();
1453 let invoice = builder.clone()
1454 .amount_pico_btc(15000)
1458 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Nano));
1459 assert_eq!(invoice.hrp.raw_amount, Some(15));
1462 let invoice = builder.clone()
1463 .amount_pico_btc(1500)
1467 assert_eq!(invoice.hrp.si_prefix, Some(SiPrefix::Pico));
1468 assert_eq!(invoice.hrp.raw_amount, Some(1500));
1472 fn test_builder_fail() {
1474 use std::iter::FromIterator;
1475 use secp256k1::key::PublicKey;
1477 let builder = InvoiceBuilder::new(Currency::Bitcoin)
1478 .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
1479 .current_timestamp();
1481 let too_long_string = String::from_iter(
1482 (0..1024).map(|_| '?')
1485 let long_desc_res = builder.clone()
1486 .description(too_long_string)
1488 assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
1490 let route_hop = RouteHintHop {
1491 src_node_id: PublicKey::from_slice(
1493 0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
1494 0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
1495 0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
1498 short_channel_id: 0,
1501 proportional_millionths: 0,
1503 cltv_expiry_delta: 0,
1504 htlc_minimum_msat: None,
1505 htlc_maximum_msat: None,
1507 let too_long_route = vec![route_hop; 13];
1508 let long_route_res = builder.clone()
1509 .description("Test".into())
1510 .route(too_long_route)
1512 assert_eq!(long_route_res, Err(CreationError::RouteTooLong));
1514 let sign_error_res = builder.clone()
1515 .description("Test".into())
1516 .try_build_signed(|_| {
1517 Err("ImaginaryError")
1519 assert_eq!(sign_error_res, Err(SignOrCreationError::SignError("ImaginaryError")));
1523 fn test_builder_ok() {
1525 use secp256k1::Secp256k1;
1526 use secp256k1::key::{SecretKey, PublicKey};
1527 use std::time::{UNIX_EPOCH, Duration};
1529 let secp_ctx = Secp256k1::new();
1531 let private_key = SecretKey::from_slice(
1533 0xe1, 0x26, 0xf6, 0x8f, 0x7e, 0xaf, 0xcc, 0x8b, 0x74, 0xf5, 0x4d, 0x26, 0x9f, 0xe2,
1534 0x06, 0xbe, 0x71, 0x50, 0x00, 0xf9, 0x4d, 0xac, 0x06, 0x7d, 0x1c, 0x04, 0xa8, 0xca,
1535 0x3b, 0x2d, 0xb7, 0x34
1538 let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
1542 src_node_id: public_key.clone(),
1543 short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
1546 proportional_millionths: 1,
1548 cltv_expiry_delta: 145,
1549 htlc_minimum_msat: None,
1550 htlc_maximum_msat: None,
1553 src_node_id: public_key.clone(),
1554 short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
1557 proportional_millionths: 2,
1559 cltv_expiry_delta: 146,
1560 htlc_minimum_msat: None,
1561 htlc_maximum_msat: None,
1567 src_node_id: public_key.clone(),
1568 short_channel_id: 0,
1571 proportional_millionths: 3,
1573 cltv_expiry_delta: 147,
1574 htlc_minimum_msat: None,
1575 htlc_maximum_msat: None,
1578 src_node_id: public_key.clone(),
1579 short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
1582 proportional_millionths: 4,
1584 cltv_expiry_delta: 148,
1585 htlc_minimum_msat: None,
1586 htlc_maximum_msat: None,
1590 let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
1591 .amount_pico_btc(123)
1592 .timestamp(UNIX_EPOCH + Duration::from_secs(1234567))
1593 .payee_pub_key(public_key.clone())
1594 .expiry_time(Duration::from_secs(54321))
1595 .min_final_cltv_expiry(144)
1596 .min_final_cltv_expiry(143)
1597 .fallback(Fallback::PubKeyHash([0;20]))
1598 .route(route_1.clone())
1599 .route(route_2.clone())
1600 .description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
1601 .payment_hash(sha256::Hash::from_slice(&[21;32][..]).unwrap());
1603 let invoice = builder.clone().build_signed(|hash| {
1604 secp_ctx.sign_recoverable(hash, &private_key)
1607 assert!(invoice.check_signature().is_ok());
1608 assert_eq!(invoice.tagged_fields().count(), 9);
1610 assert_eq!(invoice.amount_pico_btc(), Some(123));
1611 assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
1613 invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),
1616 assert_eq!(invoice.payee_pub_key(), Some(&public_key));
1617 assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
1618 assert_eq!(invoice.min_final_cltv_expiry(), Some(144));
1619 assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
1620 assert_eq!(invoice.routes(), vec![&RouteHint(route_1), &RouteHint(route_2)]);
1622 invoice.description(),
1623 InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
1625 assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&[21;32][..]).unwrap());
1627 let raw_invoice = builder.build_raw().unwrap();
1628 assert_eq!(raw_invoice, *invoice.into_signed_raw().raw_invoice())