1 // This file is Copyright its original authors, visible in version control
4 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
10 //! Data structures and encoding for static BOLT 12 invoices.
12 use crate::blinded_path::BlindedPath;
14 use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
15 use crate::ln::inbound_payment::ExpandedKey;
16 use crate::ln::msgs::DecodeError;
17 use crate::offers::invoice::{
18 check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPathIter,
19 BlindedPayInfo, BlindedPayInfoIter, FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef,
21 use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
22 use crate::offers::merkle::{
23 self, SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash,
25 use crate::offers::offer::{
26 Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef, Quantity,
28 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
29 use crate::util::ser::{
30 HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer,
32 use crate::util::string::PrintableString;
33 use bitcoin::address::Address;
34 use bitcoin::blockdata::constants::ChainHash;
35 use bitcoin::secp256k1::schnorr::Signature;
36 use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1};
37 use core::time::Duration;
39 #[cfg(feature = "std")]
40 use crate::offers::invoice::is_expired;
42 #[allow(unused_imports)]
43 use crate::prelude::*;
45 /// Static invoices default to expiring after 2 weeks.
46 const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(3600 * 24 * 14);
48 /// Tag for the hash function used when signing a [`StaticInvoice`]'s merkle root.
49 pub const SIGNATURE_TAG: &'static str = concat!("lightning", "static_invoice", "signature");
51 /// A `StaticInvoice` is a reusable payment request corresponding to an [`Offer`].
53 /// A static invoice may be sent in response to an [`InvoiceRequest`] and includes all the
54 /// information needed to pay the recipient. However, unlike [`Bolt12Invoice`]s, static invoices do
55 /// not provide proof-of-payment. Therefore, [`Bolt12Invoice`]s should be preferred when the
56 /// recipient is online to provide one.
58 /// [`Offer`]: crate::offers::offer::Offer
59 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
60 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
61 #[derive(Clone, Debug)]
62 pub struct StaticInvoice {
64 contents: InvoiceContents,
68 /// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
70 /// [`Offer`]: crate::offers::offer::Offer
71 #[derive(Clone, Debug)]
72 struct InvoiceContents {
74 payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
76 relative_expiry: Option<Duration>,
77 fallbacks: Option<Vec<FallbackAddress>>,
78 features: Bolt12InvoiceFeatures,
79 signing_pubkey: PublicKey,
80 message_paths: Vec<BlindedPath>,
83 /// Builds a [`StaticInvoice`] from an [`Offer`].
85 /// [`Offer`]: crate::offers::offer::Offer
86 /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
87 // TODO: add module-level docs and link here
88 pub struct StaticInvoiceBuilder<'a> {
89 offer_bytes: &'a Vec<u8>,
90 invoice: InvoiceContents,
94 impl<'a> StaticInvoiceBuilder<'a> {
95 /// Initialize a [`StaticInvoiceBuilder`] from the given [`Offer`].
97 /// Unless [`StaticInvoiceBuilder::relative_expiry`] is set, the invoice will expire 24 hours
98 /// after `created_at`.
99 pub fn for_offer_using_derived_keys<T: secp256k1::Signing>(
100 offer: &'a Offer, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
101 message_paths: Vec<BlindedPath>, created_at: Duration, expanded_key: &ExpandedKey,
102 secp_ctx: &Secp256k1<T>,
103 ) -> Result<Self, Bolt12SemanticError> {
104 if offer.chains().len() > 1 {
105 return Err(Bolt12SemanticError::UnexpectedChain);
108 if payment_paths.is_empty() || message_paths.is_empty() || offer.paths().is_empty() {
109 return Err(Bolt12SemanticError::MissingPaths);
112 let offer_signing_pubkey =
113 offer.signing_pubkey().ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
116 .verify(&expanded_key, &secp_ctx)
117 .map_err(|()| Bolt12SemanticError::InvalidMetadata)?
119 .ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
121 let signing_pubkey = keys.public_key();
122 if signing_pubkey != offer_signing_pubkey {
123 return Err(Bolt12SemanticError::InvalidSigningPubkey);
127 InvoiceContents::new(offer, payment_paths, message_paths, created_at, signing_pubkey);
129 Ok(Self { offer_bytes: &offer.bytes, invoice, keys })
132 /// Builds a signed [`StaticInvoice`] after checking for valid semantics.
133 pub fn build_and_sign<T: secp256k1::Signing>(
134 self, secp_ctx: &Secp256k1<T>,
135 ) -> Result<StaticInvoice, Bolt12SemanticError> {
136 #[cfg(feature = "std")]
138 if self.invoice.is_offer_expired() {
139 return Err(Bolt12SemanticError::AlreadyExpired);
143 #[cfg(not(feature = "std"))]
145 if self.invoice.is_offer_expired_no_std(self.invoice.created_at()) {
146 return Err(Bolt12SemanticError::AlreadyExpired);
150 let Self { offer_bytes, invoice, keys } = self;
151 let unsigned_invoice = UnsignedStaticInvoice::new(&offer_bytes, invoice);
152 let invoice = unsigned_invoice
153 .sign(|message: &UnsignedStaticInvoice| {
154 Ok(secp_ctx.sign_schnorr_no_aux_rand(message.tagged_hash.as_digest(), &keys))
160 invoice_builder_methods_common!(self, Self, self.invoice, Self, self, S, StaticInvoice, mut);
163 /// A semantically valid [`StaticInvoice`] that hasn't been signed.
164 pub struct UnsignedStaticInvoice {
166 contents: InvoiceContents,
167 tagged_hash: TaggedHash,
170 macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
171 /// The chain that must be used when paying the invoice. [`StaticInvoice`]s currently can only be
172 /// created from offers that support a single chain.
173 pub fn chain(&$self) -> ChainHash {
177 /// Opaque bytes set by the originating [`Offer::metadata`].
179 /// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
180 pub fn metadata(&$self) -> Option<&Vec<u8>> {
184 /// The minimum amount required for a successful payment of a single item.
186 /// From [`Offer::amount`].
188 /// [`Offer::amount`]: crate::offers::offer::Offer::amount
189 pub fn amount(&$self) -> Option<Amount> {
193 /// Features pertaining to the originating [`Offer`], from [`Offer::offer_features`].
195 /// [`Offer`]: crate::offers::offer::Offer
196 /// [`Offer::offer_features`]: crate::offers::offer::Offer::offer_features
197 pub fn offer_features(&$self) -> &OfferFeatures {
198 $contents.offer_features()
201 /// A complete description of the purpose of the originating offer, from [`Offer::description`].
203 /// [`Offer::description`]: crate::offers::offer::Offer::description
204 pub fn description(&$self) -> Option<PrintableString> {
205 $contents.description()
208 /// Duration since the Unix epoch when an invoice should no longer be requested, from
209 /// [`Offer::absolute_expiry`].
211 /// [`Offer::absolute_expiry`]: crate::offers::offer::Offer::absolute_expiry
212 pub fn absolute_expiry(&$self) -> Option<Duration> {
213 $contents.absolute_expiry()
216 /// The issuer of the offer, from [`Offer::issuer`].
218 /// [`Offer::issuer`]: crate::offers::offer::Offer::issuer
219 pub fn issuer(&$self) -> Option<PrintableString> {
223 /// Paths to the node that may supply the invoice on the recipient's behalf, originating from
224 /// publicly reachable nodes. Taken from [`Offer::paths`].
226 /// [`Offer::paths`]: crate::offers::offer::Offer::paths
227 pub fn offer_message_paths(&$self) -> &[BlindedPath] {
228 $contents.offer_message_paths()
231 /// Paths to the recipient for indicating that a held HTLC is available to claim when they next
233 pub fn message_paths(&$self) -> &[BlindedPath] {
234 $contents.message_paths()
237 /// The quantity of items supported, from [`Offer::supported_quantity`].
239 /// [`Offer::supported_quantity`]: crate::offers::offer::Offer::supported_quantity
240 pub fn supported_quantity(&$self) -> Quantity {
241 $contents.supported_quantity()
245 impl UnsignedStaticInvoice {
246 fn new(offer_bytes: &Vec<u8>, contents: InvoiceContents) -> Self {
247 let (_, invoice_tlv_stream) = contents.as_tlv_stream();
248 let offer_bytes = WithoutLength(offer_bytes);
249 let unsigned_tlv_stream = (offer_bytes, invoice_tlv_stream);
251 let mut bytes = Vec::new();
252 unsigned_tlv_stream.write(&mut bytes).unwrap();
254 let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
256 Self { contents, tagged_hash, bytes }
259 /// Signs the [`TaggedHash`] of the invoice using the given function.
261 /// Note: The hash computation may have included unknown, odd TLV records.
262 pub fn sign<F: SignStaticInvoiceFn>(mut self, sign: F) -> Result<StaticInvoice, SignError> {
263 let pubkey = self.contents.signing_pubkey;
264 let signature = merkle::sign_message(sign, &self, pubkey)?;
266 // Append the signature TLV record to the bytes.
267 let signature_tlv_stream = SignatureTlvStreamRef { signature: Some(&signature) };
268 signature_tlv_stream.write(&mut self.bytes).unwrap();
270 Ok(StaticInvoice { bytes: self.bytes, contents: self.contents, signature })
273 invoice_accessors_common!(self, self.contents, StaticInvoice);
274 invoice_accessors!(self, self.contents);
277 impl AsRef<TaggedHash> for UnsignedStaticInvoice {
278 fn as_ref(&self) -> &TaggedHash {
283 /// A function for signing an [`UnsignedStaticInvoice`].
284 pub trait SignStaticInvoiceFn {
285 /// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
286 fn sign_invoice(&self, message: &UnsignedStaticInvoice) -> Result<Signature, ()>;
289 impl<F> SignStaticInvoiceFn for F
291 F: Fn(&UnsignedStaticInvoice) -> Result<Signature, ()>,
293 fn sign_invoice(&self, message: &UnsignedStaticInvoice) -> Result<Signature, ()> {
298 impl<F> SignFn<UnsignedStaticInvoice> for F
300 F: SignStaticInvoiceFn,
302 fn sign(&self, message: &UnsignedStaticInvoice) -> Result<Signature, ()> {
303 self.sign_invoice(message)
308 invoice_accessors_common!(self, self.contents, StaticInvoice);
309 invoice_accessors!(self, self.contents);
311 /// Signature of the invoice verified using [`StaticInvoice::signing_pubkey`].
312 pub fn signature(&self) -> Signature {
317 impl InvoiceContents {
318 #[cfg(feature = "std")]
319 fn is_offer_expired(&self) -> bool {
320 self.offer.is_expired()
323 #[cfg(not(feature = "std"))]
324 fn is_offer_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
325 self.offer.is_expired_no_std(duration_since_epoch)
329 offer: &Offer, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
330 message_paths: Vec<BlindedPath>, created_at: Duration, signing_pubkey: PublicKey,
333 offer: offer.contents.clone(),
337 relative_expiry: None,
339 features: Bolt12InvoiceFeatures::empty(),
344 fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
346 if self.features == Bolt12InvoiceFeatures::empty() {
353 let invoice = InvoiceTlvStreamRef {
354 paths: Some(Iterable(self.payment_paths.iter().map(|(_, path)| path))),
355 message_paths: Some(self.message_paths.as_ref()),
356 blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| payinfo))),
357 created_at: Some(self.created_at.as_secs()),
358 relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
359 fallbacks: self.fallbacks.as_ref(),
361 node_id: Some(&self.signing_pubkey),
366 (self.offer.as_tlv_stream(), invoice)
369 fn chain(&self) -> ChainHash {
370 debug_assert_eq!(self.offer.chains().len(), 1);
371 self.offer.chains().first().cloned().unwrap_or_else(|| self.offer.implied_chain())
374 fn metadata(&self) -> Option<&Vec<u8>> {
375 self.offer.metadata()
378 fn amount(&self) -> Option<Amount> {
382 fn offer_features(&self) -> &OfferFeatures {
383 self.offer.features()
386 fn description(&self) -> Option<PrintableString> {
387 self.offer.description()
390 fn absolute_expiry(&self) -> Option<Duration> {
391 self.offer.absolute_expiry()
394 fn issuer(&self) -> Option<PrintableString> {
398 fn offer_message_paths(&self) -> &[BlindedPath] {
402 fn message_paths(&self) -> &[BlindedPath] {
403 &self.message_paths[..]
406 fn supported_quantity(&self) -> Quantity {
407 self.offer.supported_quantity()
410 fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] {
411 &self.payment_paths[..]
414 fn created_at(&self) -> Duration {
418 fn relative_expiry(&self) -> Duration {
419 self.relative_expiry.unwrap_or(DEFAULT_RELATIVE_EXPIRY)
422 #[cfg(feature = "std")]
423 fn is_expired(&self) -> bool {
424 is_expired(self.created_at(), self.relative_expiry())
427 fn fallbacks(&self) -> Vec<Address> {
428 let chain = self.chain();
431 .map(|fallbacks| filter_fallbacks(chain, fallbacks))
432 .unwrap_or_else(Vec::new)
435 fn features(&self) -> &Bolt12InvoiceFeatures {
439 fn signing_pubkey(&self) -> PublicKey {
444 impl Writeable for StaticInvoice {
445 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
446 WithoutLength(&self.bytes).write(writer)
450 impl TryFrom<Vec<u8>> for StaticInvoice {
451 type Error = Bolt12ParseError;
453 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
454 let parsed_invoice = ParsedMessage::<FullInvoiceTlvStream>::try_from(bytes)?;
455 StaticInvoice::try_from(parsed_invoice)
459 type FullInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream);
461 impl SeekReadable for FullInvoiceTlvStream {
462 fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
463 let offer = SeekReadable::read(r)?;
464 let invoice = SeekReadable::read(r)?;
465 let signature = SeekReadable::read(r)?;
467 Ok((offer, invoice, signature))
471 type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream);
473 type PartialInvoiceTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>);
475 impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
476 type Error = Bolt12ParseError;
478 fn try_from(invoice: ParsedMessage<FullInvoiceTlvStream>) -> Result<Self, Self::Error> {
479 let ParsedMessage { bytes, tlv_stream } = invoice;
480 let (offer_tlv_stream, invoice_tlv_stream, SignatureTlvStream { signature }) = tlv_stream;
481 let contents = InvoiceContents::try_from((offer_tlv_stream, invoice_tlv_stream))?;
483 let signature = match signature {
485 return Err(Bolt12ParseError::InvalidSemantics(
486 Bolt12SemanticError::MissingSignature,
489 Some(signature) => signature,
491 let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
492 let pubkey = contents.signing_pubkey;
493 merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
495 Ok(StaticInvoice { bytes, contents, signature })
499 impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
500 type Error = Bolt12SemanticError;
502 fn try_from(tlv_stream: PartialInvoiceTlvStream) -> Result<Self, Self::Error> {
519 if payment_hash.is_some() {
520 return Err(Bolt12SemanticError::UnexpectedPaymentHash);
522 if amount.is_some() {
523 return Err(Bolt12SemanticError::UnexpectedAmount);
526 let payment_paths = construct_payment_paths(blindedpay, paths)?;
527 let message_paths = message_paths.ok_or(Bolt12SemanticError::MissingPaths)?;
529 let created_at = match created_at {
530 None => return Err(Bolt12SemanticError::MissingCreationTime),
531 Some(timestamp) => Duration::from_secs(timestamp),
534 let relative_expiry = relative_expiry.map(Into::<u64>::into).map(Duration::from_secs);
536 let features = features.unwrap_or_else(Bolt12InvoiceFeatures::empty);
538 let signing_pubkey = node_id.ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
539 check_invoice_signing_pubkey(&signing_pubkey, &offer_tlv_stream)?;
541 if offer_tlv_stream.paths.is_none() {
542 return Err(Bolt12SemanticError::MissingPaths);
544 if offer_tlv_stream.chains.as_ref().map_or(0, |chains| chains.len()) > 1 {
545 return Err(Bolt12SemanticError::UnexpectedChain);
549 offer: OfferContents::try_from(offer_tlv_stream)?,
563 use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
564 use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
565 use crate::ln::inbound_payment::ExpandedKey;
566 use crate::ln::msgs::DecodeError;
567 use crate::offers::invoice::InvoiceTlvStreamRef;
568 use crate::offers::merkle;
569 use crate::offers::merkle::{SignatureTlvStreamRef, TaggedHash};
570 use crate::offers::offer::{Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
571 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
572 use crate::offers::static_invoice::{
573 StaticInvoice, StaticInvoiceBuilder, DEFAULT_RELATIVE_EXPIRY, SIGNATURE_TAG,
575 use crate::offers::test_utils::*;
576 use crate::sign::KeyMaterial;
577 use crate::util::ser::{BigSize, Iterable, Writeable};
578 use bitcoin::blockdata::constants::ChainHash;
579 use bitcoin::secp256k1::{self, Secp256k1};
580 use bitcoin::Network;
581 use core::time::Duration;
583 type FullInvoiceTlvStreamRef<'a> =
584 (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, SignatureTlvStreamRef<'a>);
587 fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
588 let (offer_tlv_stream, invoice_tlv_stream) = self.contents.as_tlv_stream();
592 SignatureTlvStreamRef { signature: Some(&self.signature) },
597 fn tlv_stream_to_bytes(
598 tlv_stream: &(OfferTlvStreamRef, InvoiceTlvStreamRef, SignatureTlvStreamRef),
600 let mut buffer = Vec::new();
601 tlv_stream.0.write(&mut buffer).unwrap();
602 tlv_stream.1.write(&mut buffer).unwrap();
603 tlv_stream.2.write(&mut buffer).unwrap();
607 fn invoice() -> StaticInvoice {
608 let node_id = recipient_pubkey();
609 let payment_paths = payment_paths();
611 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
612 let entropy = FixedEntropy {};
613 let secp_ctx = Secp256k1::new();
616 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
617 .path(blinded_path())
621 StaticInvoiceBuilder::for_offer_using_derived_keys(
623 payment_paths.clone(),
624 vec![blinded_path()],
630 .build_and_sign(&secp_ctx)
634 fn blinded_path() -> BlindedPath {
636 introduction_node: IntroductionNode::NodeId(pubkey(40)),
637 blinding_point: pubkey(41),
639 BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
640 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 44] },
646 fn builds_invoice_for_offer_with_defaults() {
647 let node_id = recipient_pubkey();
648 let payment_paths = payment_paths();
650 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
651 let entropy = FixedEntropy {};
652 let secp_ctx = Secp256k1::new();
655 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
656 .path(blinded_path())
660 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
662 payment_paths.clone(),
663 vec![blinded_path()],
669 .build_and_sign(&secp_ctx)
672 let mut buffer = Vec::new();
673 invoice.write(&mut buffer).unwrap();
675 assert_eq!(invoice.bytes, buffer.as_slice());
676 assert!(invoice.metadata().is_some());
677 assert_eq!(invoice.amount(), None);
678 assert_eq!(invoice.description(), None);
679 assert_eq!(invoice.offer_features(), &OfferFeatures::empty());
680 assert_eq!(invoice.absolute_expiry(), None);
681 assert_eq!(invoice.offer_message_paths(), &[blinded_path()]);
682 assert_eq!(invoice.message_paths(), &[blinded_path()]);
683 assert_eq!(invoice.issuer(), None);
684 assert_eq!(invoice.supported_quantity(), Quantity::One);
685 assert_ne!(invoice.signing_pubkey(), recipient_pubkey());
686 assert_eq!(invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
687 assert_eq!(invoice.payment_paths(), payment_paths.as_slice());
688 assert_eq!(invoice.created_at(), now);
689 assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY);
690 #[cfg(feature = "std")]
691 assert!(!invoice.is_expired());
692 assert!(invoice.fallbacks().is_empty());
693 assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
695 let offer_signing_pubkey = offer.signing_pubkey().unwrap();
696 let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
698 merkle::verify_signature(&invoice.signature, &message, offer_signing_pubkey).is_ok()
701 let paths = vec![blinded_path()];
702 let metadata = vec![42; 16];
704 invoice.as_tlv_stream(),
708 metadata: Some(&metadata),
713 absolute_expiry: None,
717 node_id: Some(&offer_signing_pubkey),
719 InvoiceTlvStreamRef {
720 paths: Some(Iterable(payment_paths.iter().map(|(_, path)| path))),
721 blindedpay: Some(Iterable(payment_paths.iter().map(|(payinfo, _)| payinfo))),
722 created_at: Some(now.as_secs()),
723 relative_expiry: None,
728 node_id: Some(&offer_signing_pubkey),
729 message_paths: Some(&paths),
731 SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
735 if let Err(e) = StaticInvoice::try_from(buffer) {
736 panic!("error parsing invoice: {:?}", e);
740 #[cfg(feature = "std")]
742 fn builds_invoice_from_offer_with_expiration() {
743 let node_id = recipient_pubkey();
745 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
746 let entropy = FixedEntropy {};
747 let secp_ctx = Secp256k1::new();
749 let future_expiry = Duration::from_secs(u64::max_value());
750 let past_expiry = Duration::from_secs(0);
753 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
754 .path(blinded_path())
755 .absolute_expiry(future_expiry)
759 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
762 vec![blinded_path()],
768 .build_and_sign(&secp_ctx)
770 assert!(!invoice.is_expired());
771 assert_eq!(invoice.absolute_expiry(), Some(future_expiry));
774 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
775 .path(blinded_path())
776 .absolute_expiry(past_expiry)
779 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
782 vec![blinded_path()],
788 .build_and_sign(&secp_ctx)
790 assert_eq!(e, Bolt12SemanticError::AlreadyExpired);
792 panic!("expected error")
797 fn fails_build_with_missing_paths() {
798 let node_id = recipient_pubkey();
800 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
801 let entropy = FixedEntropy {};
802 let secp_ctx = Secp256k1::new();
805 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
806 .path(blinded_path())
810 // Error if payment paths are missing.
811 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
814 vec![blinded_path()],
819 assert_eq!(e, Bolt12SemanticError::MissingPaths);
821 panic!("expected error")
824 // Error if message paths are missing.
825 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
833 assert_eq!(e, Bolt12SemanticError::MissingPaths);
835 panic!("expected error")
838 // Error if offer paths are missing.
839 let mut offer_without_paths = valid_offer.clone();
840 let mut offer_tlv_stream = offer_without_paths.as_tlv_stream();
841 offer_tlv_stream.paths.take();
842 let mut buffer = Vec::new();
843 offer_tlv_stream.write(&mut buffer).unwrap();
844 offer_without_paths = Offer::try_from(buffer).unwrap();
845 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
846 &offer_without_paths,
848 vec![blinded_path()],
853 assert_eq!(e, Bolt12SemanticError::MissingPaths);
855 panic!("expected error")
860 fn fails_build_offer_signing_pubkey() {
861 let node_id = recipient_pubkey();
863 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
864 let entropy = FixedEntropy {};
865 let secp_ctx = Secp256k1::new();
868 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
869 .path(blinded_path())
873 // Error if offer signing pubkey is missing.
874 let mut offer_missing_signing_pubkey = valid_offer.clone();
875 let mut offer_tlv_stream = offer_missing_signing_pubkey.as_tlv_stream();
876 offer_tlv_stream.node_id.take();
877 let mut buffer = Vec::new();
878 offer_tlv_stream.write(&mut buffer).unwrap();
879 offer_missing_signing_pubkey = Offer::try_from(buffer).unwrap();
881 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
882 &offer_missing_signing_pubkey,
884 vec![blinded_path()],
889 assert_eq!(e, Bolt12SemanticError::MissingSigningPubkey);
891 panic!("expected error")
894 // Error if the offer's metadata cannot be verified.
895 let offer = OfferBuilder::new(recipient_pubkey())
896 .path(blinded_path())
897 .metadata(vec![42; 32])
901 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
904 vec![blinded_path()],
909 assert_eq!(e, Bolt12SemanticError::InvalidMetadata);
911 panic!("expected error")
916 fn fails_building_with_extra_offer_chains() {
917 let node_id = recipient_pubkey();
919 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
920 let entropy = FixedEntropy {};
921 let secp_ctx = Secp256k1::new();
923 let offer_with_extra_chain =
924 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
925 .path(blinded_path())
926 .chain(Network::Bitcoin)
927 .chain(Network::Testnet)
931 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
932 &offer_with_extra_chain,
934 vec![blinded_path()],
939 assert_eq!(e, Bolt12SemanticError::UnexpectedChain);
941 panic!("expected error")
946 fn parses_invoice_with_relative_expiry() {
947 let node_id = recipient_pubkey();
948 let payment_paths = payment_paths();
950 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
951 let entropy = FixedEntropy {};
952 let secp_ctx = Secp256k1::new();
955 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
956 .path(blinded_path())
960 const TEST_RELATIVE_EXPIRY: u32 = 3600;
961 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
963 payment_paths.clone(),
964 vec![blinded_path()],
970 .relative_expiry(TEST_RELATIVE_EXPIRY)
971 .build_and_sign(&secp_ctx)
974 let mut buffer = Vec::new();
975 invoice.write(&mut buffer).unwrap();
977 match StaticInvoice::try_from(buffer) {
978 Ok(invoice) => assert_eq!(
979 invoice.relative_expiry(),
980 Duration::from_secs(TEST_RELATIVE_EXPIRY as u64)
982 Err(e) => panic!("error parsing invoice: {:?}", e),
987 fn parses_invoice_with_allow_mpp() {
988 let node_id = recipient_pubkey();
989 let payment_paths = payment_paths();
991 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
992 let entropy = FixedEntropy {};
993 let secp_ctx = Secp256k1::new();
996 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
997 .path(blinded_path())
1001 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
1003 payment_paths.clone(),
1004 vec![blinded_path()],
1011 .build_and_sign(&secp_ctx)
1014 let mut buffer = Vec::new();
1015 invoice.write(&mut buffer).unwrap();
1017 match StaticInvoice::try_from(buffer) {
1019 let mut features = Bolt12InvoiceFeatures::empty();
1020 features.set_basic_mpp_optional();
1021 assert_eq!(invoice.invoice_features(), &features);
1023 Err(e) => panic!("error parsing invoice: {:?}", e),
1028 fn fails_parsing_missing_invoice_fields() {
1029 // Error if `created_at` is missing.
1030 let missing_created_at_invoice = invoice();
1031 let mut tlv_stream = missing_created_at_invoice.as_tlv_stream();
1032 tlv_stream.1.created_at = None;
1033 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1034 Ok(_) => panic!("expected error"),
1038 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingCreationTime)
1043 // Error if `node_id` is missing.
1044 let missing_node_id_invoice = invoice();
1045 let mut tlv_stream = missing_node_id_invoice.as_tlv_stream();
1046 tlv_stream.1.node_id = None;
1047 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1048 Ok(_) => panic!("expected error"),
1052 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)
1057 // Error if message paths are missing.
1058 let missing_message_paths_invoice = invoice();
1059 let mut tlv_stream = missing_message_paths_invoice.as_tlv_stream();
1060 tlv_stream.1.message_paths = None;
1061 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1062 Ok(_) => panic!("expected error"),
1066 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths)
1071 // Error if signature is missing.
1072 let invoice = invoice();
1073 let mut buffer = Vec::new();
1074 invoice.contents.as_tlv_stream().write(&mut buffer).unwrap();
1075 match StaticInvoice::try_from(buffer) {
1076 Ok(_) => panic!("expected error"),
1077 Err(e) => assert_eq!(
1079 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)
1085 fn fails_parsing_invalid_signing_pubkey() {
1086 let invoice = invoice();
1087 let invalid_pubkey = payer_pubkey();
1088 let mut tlv_stream = invoice.as_tlv_stream();
1089 tlv_stream.1.node_id = Some(&invalid_pubkey);
1091 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1092 Ok(_) => panic!("expected error"),
1096 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidSigningPubkey)
1103 fn fails_parsing_invoice_with_invalid_signature() {
1104 let mut invoice = invoice();
1105 let last_signature_byte = invoice.bytes.last_mut().unwrap();
1106 *last_signature_byte = last_signature_byte.wrapping_add(1);
1108 let mut buffer = Vec::new();
1109 invoice.write(&mut buffer).unwrap();
1111 match StaticInvoice::try_from(buffer) {
1112 Ok(_) => panic!("expected error"),
1116 Bolt12ParseError::InvalidSignature(secp256k1::Error::InvalidSignature)
1123 fn fails_parsing_invoice_with_extra_tlv_records() {
1124 let invoice = invoice();
1125 let mut encoded_invoice = Vec::new();
1126 invoice.write(&mut encoded_invoice).unwrap();
1127 BigSize(1002).write(&mut encoded_invoice).unwrap();
1128 BigSize(32).write(&mut encoded_invoice).unwrap();
1129 [42u8; 32].write(&mut encoded_invoice).unwrap();
1131 match StaticInvoice::try_from(encoded_invoice) {
1132 Ok(_) => panic!("expected error"),
1133 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1138 fn fails_parsing_invoice_with_invalid_offer_fields() {
1139 // Error if the offer is missing paths.
1140 let missing_offer_paths_invoice = invoice();
1141 let mut tlv_stream = missing_offer_paths_invoice.as_tlv_stream();
1142 tlv_stream.0.paths = None;
1143 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1144 Ok(_) => panic!("expected error"),
1148 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths)
1153 // Error if the offer has more than one chain.
1154 let invalid_offer_chains_invoice = invoice();
1155 let mut tlv_stream = invalid_offer_chains_invoice.as_tlv_stream();
1156 let invalid_chains = vec![
1157 ChainHash::using_genesis_block(Network::Bitcoin),
1158 ChainHash::using_genesis_block(Network::Testnet),
1160 tlv_stream.0.chains = Some(&invalid_chains);
1161 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1162 Ok(_) => panic!("expected error"),
1166 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedChain)