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, BlindedPayInfo,
19 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::{Iterable, SeekReadable, WithoutLength, Writeable, Writer};
30 use crate::util::string::PrintableString;
31 use bitcoin::address::Address;
32 use bitcoin::blockdata::constants::ChainHash;
33 use bitcoin::secp256k1::schnorr::Signature;
34 use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1};
35 use core::time::Duration;
37 #[cfg(feature = "std")]
38 use crate::offers::invoice::is_expired;
40 #[allow(unused_imports)]
41 use crate::prelude::*;
43 /// Static invoices default to expiring after 2 weeks.
44 const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(3600 * 24 * 14);
46 /// Tag for the hash function used when signing a [`StaticInvoice`]'s merkle root.
47 pub const SIGNATURE_TAG: &'static str = concat!("lightning", "static_invoice", "signature");
49 /// A `StaticInvoice` is a reusable payment request corresponding to an [`Offer`].
51 /// A static invoice may be sent in response to an [`InvoiceRequest`] and includes all the
52 /// information needed to pay the recipient. However, unlike [`Bolt12Invoice`]s, static invoices do
53 /// not provide proof-of-payment. Therefore, [`Bolt12Invoice`]s should be preferred when the
54 /// recipient is online to provide one.
56 /// [`Offer`]: crate::offers::offer::Offer
57 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
58 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
59 #[derive(Clone, Debug)]
60 pub struct StaticInvoice {
62 contents: InvoiceContents,
66 /// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
68 /// [`Offer`]: crate::offers::offer::Offer
69 #[derive(Clone, Debug)]
70 struct InvoiceContents {
72 payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
74 relative_expiry: Option<Duration>,
75 fallbacks: Option<Vec<FallbackAddress>>,
76 features: Bolt12InvoiceFeatures,
77 signing_pubkey: PublicKey,
78 message_paths: Vec<BlindedPath>,
81 /// Builds a [`StaticInvoice`] from an [`Offer`].
83 /// [`Offer`]: crate::offers::offer::Offer
84 /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
85 // TODO: add module-level docs and link here
86 pub struct StaticInvoiceBuilder<'a> {
87 offer_bytes: &'a Vec<u8>,
88 invoice: InvoiceContents,
92 impl<'a> StaticInvoiceBuilder<'a> {
93 /// Initialize a [`StaticInvoiceBuilder`] from the given [`Offer`].
95 /// Unless [`StaticInvoiceBuilder::relative_expiry`] is set, the invoice will expire 24 hours
96 /// after `created_at`.
97 pub fn for_offer_using_derived_keys<T: secp256k1::Signing>(
98 offer: &'a Offer, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
99 message_paths: Vec<BlindedPath>, created_at: Duration, expanded_key: &ExpandedKey,
100 secp_ctx: &Secp256k1<T>,
101 ) -> Result<Self, Bolt12SemanticError> {
102 if offer.chains().len() > 1 {
103 return Err(Bolt12SemanticError::UnexpectedChain);
106 if payment_paths.is_empty() || message_paths.is_empty() || offer.paths().is_empty() {
107 return Err(Bolt12SemanticError::MissingPaths);
110 let offer_signing_pubkey =
111 offer.signing_pubkey().ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
114 .verify(&expanded_key, &secp_ctx)
115 .map_err(|()| Bolt12SemanticError::InvalidMetadata)?
117 .ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
119 let signing_pubkey = keys.public_key();
120 if signing_pubkey != offer_signing_pubkey {
121 return Err(Bolt12SemanticError::InvalidSigningPubkey);
125 InvoiceContents::new(offer, payment_paths, message_paths, created_at, signing_pubkey);
127 Ok(Self { offer_bytes: &offer.bytes, invoice, keys })
130 /// Builds a signed [`StaticInvoice`] after checking for valid semantics.
131 pub fn build_and_sign<T: secp256k1::Signing>(
132 self, secp_ctx: &Secp256k1<T>,
133 ) -> Result<StaticInvoice, Bolt12SemanticError> {
134 #[cfg(feature = "std")]
136 if self.invoice.is_offer_expired() {
137 return Err(Bolt12SemanticError::AlreadyExpired);
141 #[cfg(not(feature = "std"))]
143 if self.invoice.is_offer_expired_no_std(self.invoice.created_at()) {
144 return Err(Bolt12SemanticError::AlreadyExpired);
148 let Self { offer_bytes, invoice, keys } = self;
149 let unsigned_invoice = UnsignedStaticInvoice::new(&offer_bytes, invoice);
150 let invoice = unsigned_invoice
151 .sign(|message: &UnsignedStaticInvoice| {
152 Ok(secp_ctx.sign_schnorr_no_aux_rand(message.tagged_hash.as_digest(), &keys))
158 invoice_builder_methods_common!(self, Self, self.invoice, Self, self, S, StaticInvoice, mut);
161 /// A semantically valid [`StaticInvoice`] that hasn't been signed.
162 pub struct UnsignedStaticInvoice {
164 contents: InvoiceContents,
165 tagged_hash: TaggedHash,
168 macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
169 /// The chain that must be used when paying the invoice. [`StaticInvoice`]s currently can only be
170 /// created from offers that support a single chain.
171 pub fn chain(&$self) -> ChainHash {
175 /// Opaque bytes set by the originating [`Offer::metadata`].
177 /// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
178 pub fn metadata(&$self) -> Option<&Vec<u8>> {
182 /// The minimum amount required for a successful payment of a single item.
184 /// From [`Offer::amount`].
186 /// [`Offer::amount`]: crate::offers::offer::Offer::amount
187 pub fn amount(&$self) -> Option<Amount> {
191 /// Features pertaining to the originating [`Offer`], from [`Offer::offer_features`].
193 /// [`Offer`]: crate::offers::offer::Offer
194 /// [`Offer::offer_features`]: crate::offers::offer::Offer::offer_features
195 pub fn offer_features(&$self) -> &OfferFeatures {
196 $contents.offer_features()
199 /// A complete description of the purpose of the originating offer, from [`Offer::description`].
201 /// [`Offer::description`]: crate::offers::offer::Offer::description
202 pub fn description(&$self) -> Option<PrintableString> {
203 $contents.description()
206 /// Duration since the Unix epoch when an invoice should no longer be requested, from
207 /// [`Offer::absolute_expiry`].
209 /// [`Offer::absolute_expiry`]: crate::offers::offer::Offer::absolute_expiry
210 pub fn absolute_expiry(&$self) -> Option<Duration> {
211 $contents.absolute_expiry()
214 /// The issuer of the offer, from [`Offer::issuer`].
216 /// [`Offer::issuer`]: crate::offers::offer::Offer::issuer
217 pub fn issuer(&$self) -> Option<PrintableString> {
221 /// Paths to the node that may supply the invoice on the recipient's behalf, originating from
222 /// publicly reachable nodes. Taken from [`Offer::paths`].
224 /// [`Offer::paths`]: crate::offers::offer::Offer::paths
225 pub fn offer_message_paths(&$self) -> &[BlindedPath] {
226 $contents.offer_message_paths()
229 /// Paths to the recipient for indicating that a held HTLC is available to claim when they next
231 pub fn message_paths(&$self) -> &[BlindedPath] {
232 $contents.message_paths()
235 /// The quantity of items supported, from [`Offer::supported_quantity`].
237 /// [`Offer::supported_quantity`]: crate::offers::offer::Offer::supported_quantity
238 pub fn supported_quantity(&$self) -> Quantity {
239 $contents.supported_quantity()
243 impl UnsignedStaticInvoice {
244 fn new(offer_bytes: &Vec<u8>, contents: InvoiceContents) -> Self {
245 let (_, invoice_tlv_stream) = contents.as_tlv_stream();
246 let offer_bytes = WithoutLength(offer_bytes);
247 let unsigned_tlv_stream = (offer_bytes, invoice_tlv_stream);
249 let mut bytes = Vec::new();
250 unsigned_tlv_stream.write(&mut bytes).unwrap();
252 let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
254 Self { contents, tagged_hash, bytes }
257 /// Signs the [`TaggedHash`] of the invoice using the given function.
259 /// Note: The hash computation may have included unknown, odd TLV records.
260 pub fn sign<F: SignStaticInvoiceFn>(mut self, sign: F) -> Result<StaticInvoice, SignError> {
261 let pubkey = self.contents.signing_pubkey;
262 let signature = merkle::sign_message(sign, &self, pubkey)?;
264 // Append the signature TLV record to the bytes.
265 let signature_tlv_stream = SignatureTlvStreamRef { signature: Some(&signature) };
266 signature_tlv_stream.write(&mut self.bytes).unwrap();
268 Ok(StaticInvoice { bytes: self.bytes, contents: self.contents, signature })
271 invoice_accessors_common!(self, self.contents, StaticInvoice);
272 invoice_accessors!(self, self.contents);
275 impl AsRef<TaggedHash> for UnsignedStaticInvoice {
276 fn as_ref(&self) -> &TaggedHash {
281 /// A function for signing an [`UnsignedStaticInvoice`].
282 pub trait SignStaticInvoiceFn {
283 /// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
284 fn sign_invoice(&self, message: &UnsignedStaticInvoice) -> Result<Signature, ()>;
287 impl<F> SignStaticInvoiceFn for F
289 F: Fn(&UnsignedStaticInvoice) -> Result<Signature, ()>,
291 fn sign_invoice(&self, message: &UnsignedStaticInvoice) -> Result<Signature, ()> {
296 impl<F> SignFn<UnsignedStaticInvoice> for F
298 F: SignStaticInvoiceFn,
300 fn sign(&self, message: &UnsignedStaticInvoice) -> Result<Signature, ()> {
301 self.sign_invoice(message)
306 invoice_accessors_common!(self, self.contents, StaticInvoice);
307 invoice_accessors!(self, self.contents);
309 /// Signature of the invoice verified using [`StaticInvoice::signing_pubkey`].
310 pub fn signature(&self) -> Signature {
315 impl InvoiceContents {
316 #[cfg(feature = "std")]
317 fn is_offer_expired(&self) -> bool {
318 self.offer.is_expired()
321 #[cfg(not(feature = "std"))]
322 fn is_offer_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
323 self.offer.is_expired_no_std(duration_since_epoch)
327 offer: &Offer, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
328 message_paths: Vec<BlindedPath>, created_at: Duration, signing_pubkey: PublicKey,
331 offer: offer.contents.clone(),
335 relative_expiry: None,
337 features: Bolt12InvoiceFeatures::empty(),
342 fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
344 if self.features == Bolt12InvoiceFeatures::empty() {
351 let invoice = InvoiceTlvStreamRef {
352 paths: Some(Iterable(self.payment_paths.iter().map(|(_, path)| path))),
353 message_paths: Some(self.message_paths.as_ref()),
354 blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| payinfo))),
355 created_at: Some(self.created_at.as_secs()),
356 relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
357 fallbacks: self.fallbacks.as_ref(),
359 node_id: Some(&self.signing_pubkey),
364 (self.offer.as_tlv_stream(), invoice)
367 fn chain(&self) -> ChainHash {
368 debug_assert_eq!(self.offer.chains().len(), 1);
369 self.offer.chains().first().cloned().unwrap_or_else(|| self.offer.implied_chain())
372 fn metadata(&self) -> Option<&Vec<u8>> {
373 self.offer.metadata()
376 fn amount(&self) -> Option<Amount> {
380 fn offer_features(&self) -> &OfferFeatures {
381 self.offer.features()
384 fn description(&self) -> Option<PrintableString> {
385 self.offer.description()
388 fn absolute_expiry(&self) -> Option<Duration> {
389 self.offer.absolute_expiry()
392 fn issuer(&self) -> Option<PrintableString> {
396 fn offer_message_paths(&self) -> &[BlindedPath] {
400 fn message_paths(&self) -> &[BlindedPath] {
401 &self.message_paths[..]
404 fn supported_quantity(&self) -> Quantity {
405 self.offer.supported_quantity()
408 fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] {
409 &self.payment_paths[..]
412 fn created_at(&self) -> Duration {
416 fn relative_expiry(&self) -> Duration {
417 self.relative_expiry.unwrap_or(DEFAULT_RELATIVE_EXPIRY)
420 #[cfg(feature = "std")]
421 fn is_expired(&self) -> bool {
422 is_expired(self.created_at(), self.relative_expiry())
425 fn fallbacks(&self) -> Vec<Address> {
426 let chain = self.chain();
429 .map(|fallbacks| filter_fallbacks(chain, fallbacks))
430 .unwrap_or_else(Vec::new)
433 fn features(&self) -> &Bolt12InvoiceFeatures {
437 fn signing_pubkey(&self) -> PublicKey {
442 impl Writeable for StaticInvoice {
443 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
444 WithoutLength(&self.bytes).write(writer)
448 impl TryFrom<Vec<u8>> for StaticInvoice {
449 type Error = Bolt12ParseError;
451 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
452 let parsed_invoice = ParsedMessage::<FullInvoiceTlvStream>::try_from(bytes)?;
453 StaticInvoice::try_from(parsed_invoice)
457 type FullInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream);
459 impl SeekReadable for FullInvoiceTlvStream {
460 fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
461 let offer = SeekReadable::read(r)?;
462 let invoice = SeekReadable::read(r)?;
463 let signature = SeekReadable::read(r)?;
465 Ok((offer, invoice, signature))
469 type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream);
471 type PartialInvoiceTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>);
473 impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
474 type Error = Bolt12ParseError;
476 fn try_from(invoice: ParsedMessage<FullInvoiceTlvStream>) -> Result<Self, Self::Error> {
477 let ParsedMessage { bytes, tlv_stream } = invoice;
478 let (offer_tlv_stream, invoice_tlv_stream, SignatureTlvStream { signature }) = tlv_stream;
479 let contents = InvoiceContents::try_from((offer_tlv_stream, invoice_tlv_stream))?;
481 let signature = match signature {
483 return Err(Bolt12ParseError::InvalidSemantics(
484 Bolt12SemanticError::MissingSignature,
487 Some(signature) => signature,
489 let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
490 let pubkey = contents.signing_pubkey;
491 merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
493 Ok(StaticInvoice { bytes, contents, signature })
497 impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
498 type Error = Bolt12SemanticError;
500 fn try_from(tlv_stream: PartialInvoiceTlvStream) -> Result<Self, Self::Error> {
517 if payment_hash.is_some() {
518 return Err(Bolt12SemanticError::UnexpectedPaymentHash);
520 if amount.is_some() {
521 return Err(Bolt12SemanticError::UnexpectedAmount);
524 let payment_paths = construct_payment_paths(blindedpay, paths)?;
525 let message_paths = message_paths.ok_or(Bolt12SemanticError::MissingPaths)?;
527 let created_at = match created_at {
528 None => return Err(Bolt12SemanticError::MissingCreationTime),
529 Some(timestamp) => Duration::from_secs(timestamp),
532 let relative_expiry = relative_expiry.map(Into::<u64>::into).map(Duration::from_secs);
534 let features = features.unwrap_or_else(Bolt12InvoiceFeatures::empty);
536 let signing_pubkey = node_id.ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
537 check_invoice_signing_pubkey(&signing_pubkey, &offer_tlv_stream)?;
539 if offer_tlv_stream.paths.is_none() {
540 return Err(Bolt12SemanticError::MissingPaths);
542 if offer_tlv_stream.chains.as_ref().map_or(0, |chains| chains.len()) > 1 {
543 return Err(Bolt12SemanticError::UnexpectedChain);
547 offer: OfferContents::try_from(offer_tlv_stream)?,
561 use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
562 use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
563 use crate::ln::inbound_payment::ExpandedKey;
564 use crate::ln::msgs::DecodeError;
565 use crate::offers::invoice::InvoiceTlvStreamRef;
566 use crate::offers::merkle;
567 use crate::offers::merkle::{SignatureTlvStreamRef, TaggedHash};
568 use crate::offers::offer::{Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
569 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
570 use crate::offers::static_invoice::{
571 StaticInvoice, StaticInvoiceBuilder, DEFAULT_RELATIVE_EXPIRY, SIGNATURE_TAG,
573 use crate::offers::test_utils::*;
574 use crate::sign::KeyMaterial;
575 use crate::util::ser::{BigSize, Iterable, Writeable};
576 use bitcoin::blockdata::constants::ChainHash;
577 use bitcoin::secp256k1::{self, Secp256k1};
578 use bitcoin::Network;
579 use core::time::Duration;
581 type FullInvoiceTlvStreamRef<'a> =
582 (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, SignatureTlvStreamRef<'a>);
585 fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
586 let (offer_tlv_stream, invoice_tlv_stream) = self.contents.as_tlv_stream();
590 SignatureTlvStreamRef { signature: Some(&self.signature) },
595 fn tlv_stream_to_bytes(
596 tlv_stream: &(OfferTlvStreamRef, InvoiceTlvStreamRef, SignatureTlvStreamRef),
598 let mut buffer = Vec::new();
599 tlv_stream.0.write(&mut buffer).unwrap();
600 tlv_stream.1.write(&mut buffer).unwrap();
601 tlv_stream.2.write(&mut buffer).unwrap();
605 fn invoice() -> StaticInvoice {
606 let node_id = recipient_pubkey();
607 let payment_paths = payment_paths();
609 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
610 let entropy = FixedEntropy {};
611 let secp_ctx = Secp256k1::new();
614 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
615 .path(blinded_path())
619 StaticInvoiceBuilder::for_offer_using_derived_keys(
621 payment_paths.clone(),
622 vec![blinded_path()],
628 .build_and_sign(&secp_ctx)
632 fn blinded_path() -> BlindedPath {
634 introduction_node: IntroductionNode::NodeId(pubkey(40)),
635 blinding_point: pubkey(41),
637 BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
638 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 44] },
644 fn builds_invoice_for_offer_with_defaults() {
645 let node_id = recipient_pubkey();
646 let payment_paths = payment_paths();
648 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
649 let entropy = FixedEntropy {};
650 let secp_ctx = Secp256k1::new();
653 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
654 .path(blinded_path())
658 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
660 payment_paths.clone(),
661 vec![blinded_path()],
667 .build_and_sign(&secp_ctx)
670 let mut buffer = Vec::new();
671 invoice.write(&mut buffer).unwrap();
673 assert_eq!(invoice.bytes, buffer.as_slice());
674 assert!(invoice.metadata().is_some());
675 assert_eq!(invoice.amount(), None);
676 assert_eq!(invoice.description(), None);
677 assert_eq!(invoice.offer_features(), &OfferFeatures::empty());
678 assert_eq!(invoice.absolute_expiry(), None);
679 assert_eq!(invoice.offer_message_paths(), &[blinded_path()]);
680 assert_eq!(invoice.message_paths(), &[blinded_path()]);
681 assert_eq!(invoice.issuer(), None);
682 assert_eq!(invoice.supported_quantity(), Quantity::One);
683 assert_ne!(invoice.signing_pubkey(), recipient_pubkey());
684 assert_eq!(invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
685 assert_eq!(invoice.payment_paths(), payment_paths.as_slice());
686 assert_eq!(invoice.created_at(), now);
687 assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY);
688 #[cfg(feature = "std")]
689 assert!(!invoice.is_expired());
690 assert!(invoice.fallbacks().is_empty());
691 assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
693 let offer_signing_pubkey = offer.signing_pubkey().unwrap();
694 let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
696 merkle::verify_signature(&invoice.signature, &message, offer_signing_pubkey).is_ok()
699 let paths = vec![blinded_path()];
700 let metadata = vec![42; 16];
702 invoice.as_tlv_stream(),
706 metadata: Some(&metadata),
711 absolute_expiry: None,
715 node_id: Some(&offer_signing_pubkey),
717 InvoiceTlvStreamRef {
718 paths: Some(Iterable(payment_paths.iter().map(|(_, path)| path))),
719 blindedpay: Some(Iterable(payment_paths.iter().map(|(payinfo, _)| payinfo))),
720 created_at: Some(now.as_secs()),
721 relative_expiry: None,
726 node_id: Some(&offer_signing_pubkey),
727 message_paths: Some(&paths),
729 SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
733 if let Err(e) = StaticInvoice::try_from(buffer) {
734 panic!("error parsing invoice: {:?}", e);
738 #[cfg(feature = "std")]
740 fn builds_invoice_from_offer_with_expiration() {
741 let node_id = recipient_pubkey();
743 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
744 let entropy = FixedEntropy {};
745 let secp_ctx = Secp256k1::new();
747 let future_expiry = Duration::from_secs(u64::max_value());
748 let past_expiry = Duration::from_secs(0);
751 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
752 .path(blinded_path())
753 .absolute_expiry(future_expiry)
757 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
760 vec![blinded_path()],
766 .build_and_sign(&secp_ctx)
768 assert!(!invoice.is_expired());
769 assert_eq!(invoice.absolute_expiry(), Some(future_expiry));
772 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
773 .path(blinded_path())
774 .absolute_expiry(past_expiry)
777 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
780 vec![blinded_path()],
786 .build_and_sign(&secp_ctx)
788 assert_eq!(e, Bolt12SemanticError::AlreadyExpired);
790 panic!("expected error")
795 fn fails_build_with_missing_paths() {
796 let node_id = recipient_pubkey();
798 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
799 let entropy = FixedEntropy {};
800 let secp_ctx = Secp256k1::new();
803 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
804 .path(blinded_path())
808 // Error if payment paths are missing.
809 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
812 vec![blinded_path()],
817 assert_eq!(e, Bolt12SemanticError::MissingPaths);
819 panic!("expected error")
822 // Error if message paths are missing.
823 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
831 assert_eq!(e, Bolt12SemanticError::MissingPaths);
833 panic!("expected error")
836 // Error if offer paths are missing.
837 let mut offer_without_paths = valid_offer.clone();
838 let mut offer_tlv_stream = offer_without_paths.as_tlv_stream();
839 offer_tlv_stream.paths.take();
840 let mut buffer = Vec::new();
841 offer_tlv_stream.write(&mut buffer).unwrap();
842 offer_without_paths = Offer::try_from(buffer).unwrap();
843 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
844 &offer_without_paths,
846 vec![blinded_path()],
851 assert_eq!(e, Bolt12SemanticError::MissingPaths);
853 panic!("expected error")
858 fn fails_build_offer_signing_pubkey() {
859 let node_id = recipient_pubkey();
861 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
862 let entropy = FixedEntropy {};
863 let secp_ctx = Secp256k1::new();
866 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
867 .path(blinded_path())
871 // Error if offer signing pubkey is missing.
872 let mut offer_missing_signing_pubkey = valid_offer.clone();
873 let mut offer_tlv_stream = offer_missing_signing_pubkey.as_tlv_stream();
874 offer_tlv_stream.node_id.take();
875 let mut buffer = Vec::new();
876 offer_tlv_stream.write(&mut buffer).unwrap();
877 offer_missing_signing_pubkey = Offer::try_from(buffer).unwrap();
879 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
880 &offer_missing_signing_pubkey,
882 vec![blinded_path()],
887 assert_eq!(e, Bolt12SemanticError::MissingSigningPubkey);
889 panic!("expected error")
892 // Error if the offer's metadata cannot be verified.
893 let offer = OfferBuilder::new(recipient_pubkey())
894 .path(blinded_path())
895 .metadata(vec![42; 32])
899 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
902 vec![blinded_path()],
907 assert_eq!(e, Bolt12SemanticError::InvalidMetadata);
909 panic!("expected error")
914 fn fails_building_with_extra_offer_chains() {
915 let node_id = recipient_pubkey();
917 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
918 let entropy = FixedEntropy {};
919 let secp_ctx = Secp256k1::new();
921 let offer_with_extra_chain =
922 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
923 .path(blinded_path())
924 .chain(Network::Bitcoin)
925 .chain(Network::Testnet)
929 if let Err(e) = StaticInvoiceBuilder::for_offer_using_derived_keys(
930 &offer_with_extra_chain,
932 vec![blinded_path()],
937 assert_eq!(e, Bolt12SemanticError::UnexpectedChain);
939 panic!("expected error")
944 fn parses_invoice_with_relative_expiry() {
945 let node_id = recipient_pubkey();
946 let payment_paths = payment_paths();
948 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
949 let entropy = FixedEntropy {};
950 let secp_ctx = Secp256k1::new();
953 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
954 .path(blinded_path())
958 const TEST_RELATIVE_EXPIRY: u32 = 3600;
959 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
961 payment_paths.clone(),
962 vec![blinded_path()],
968 .relative_expiry(TEST_RELATIVE_EXPIRY)
969 .build_and_sign(&secp_ctx)
972 let mut buffer = Vec::new();
973 invoice.write(&mut buffer).unwrap();
975 match StaticInvoice::try_from(buffer) {
976 Ok(invoice) => assert_eq!(
977 invoice.relative_expiry(),
978 Duration::from_secs(TEST_RELATIVE_EXPIRY as u64)
980 Err(e) => panic!("error parsing invoice: {:?}", e),
985 fn parses_invoice_with_allow_mpp() {
986 let node_id = recipient_pubkey();
987 let payment_paths = payment_paths();
989 let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
990 let entropy = FixedEntropy {};
991 let secp_ctx = Secp256k1::new();
994 OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
995 .path(blinded_path())
999 let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
1001 payment_paths.clone(),
1002 vec![blinded_path()],
1009 .build_and_sign(&secp_ctx)
1012 let mut buffer = Vec::new();
1013 invoice.write(&mut buffer).unwrap();
1015 match StaticInvoice::try_from(buffer) {
1017 let mut features = Bolt12InvoiceFeatures::empty();
1018 features.set_basic_mpp_optional();
1019 assert_eq!(invoice.invoice_features(), &features);
1021 Err(e) => panic!("error parsing invoice: {:?}", e),
1026 fn fails_parsing_missing_invoice_fields() {
1027 // Error if `created_at` is missing.
1028 let missing_created_at_invoice = invoice();
1029 let mut tlv_stream = missing_created_at_invoice.as_tlv_stream();
1030 tlv_stream.1.created_at = None;
1031 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1032 Ok(_) => panic!("expected error"),
1036 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingCreationTime)
1041 // Error if `node_id` is missing.
1042 let missing_node_id_invoice = invoice();
1043 let mut tlv_stream = missing_node_id_invoice.as_tlv_stream();
1044 tlv_stream.1.node_id = None;
1045 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1046 Ok(_) => panic!("expected error"),
1050 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)
1055 // Error if message paths are missing.
1056 let missing_message_paths_invoice = invoice();
1057 let mut tlv_stream = missing_message_paths_invoice.as_tlv_stream();
1058 tlv_stream.1.message_paths = None;
1059 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1060 Ok(_) => panic!("expected error"),
1064 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths)
1069 // Error if signature is missing.
1070 let invoice = invoice();
1071 let mut buffer = Vec::new();
1072 invoice.contents.as_tlv_stream().write(&mut buffer).unwrap();
1073 match StaticInvoice::try_from(buffer) {
1074 Ok(_) => panic!("expected error"),
1075 Err(e) => assert_eq!(
1077 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)
1083 fn fails_parsing_invalid_signing_pubkey() {
1084 let invoice = invoice();
1085 let invalid_pubkey = payer_pubkey();
1086 let mut tlv_stream = invoice.as_tlv_stream();
1087 tlv_stream.1.node_id = Some(&invalid_pubkey);
1089 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1090 Ok(_) => panic!("expected error"),
1094 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidSigningPubkey)
1101 fn fails_parsing_invoice_with_invalid_signature() {
1102 let mut invoice = invoice();
1103 let last_signature_byte = invoice.bytes.last_mut().unwrap();
1104 *last_signature_byte = last_signature_byte.wrapping_add(1);
1106 let mut buffer = Vec::new();
1107 invoice.write(&mut buffer).unwrap();
1109 match StaticInvoice::try_from(buffer) {
1110 Ok(_) => panic!("expected error"),
1114 Bolt12ParseError::InvalidSignature(secp256k1::Error::InvalidSignature)
1121 fn fails_parsing_invoice_with_extra_tlv_records() {
1122 let invoice = invoice();
1123 let mut encoded_invoice = Vec::new();
1124 invoice.write(&mut encoded_invoice).unwrap();
1125 BigSize(1002).write(&mut encoded_invoice).unwrap();
1126 BigSize(32).write(&mut encoded_invoice).unwrap();
1127 [42u8; 32].write(&mut encoded_invoice).unwrap();
1129 match StaticInvoice::try_from(encoded_invoice) {
1130 Ok(_) => panic!("expected error"),
1131 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1136 fn fails_parsing_invoice_with_invalid_offer_fields() {
1137 // Error if the offer is missing paths.
1138 let missing_offer_paths_invoice = invoice();
1139 let mut tlv_stream = missing_offer_paths_invoice.as_tlv_stream();
1140 tlv_stream.0.paths = None;
1141 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1142 Ok(_) => panic!("expected error"),
1146 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths)
1151 // Error if the offer has more than one chain.
1152 let invalid_offer_chains_invoice = invoice();
1153 let mut tlv_stream = invalid_offer_chains_invoice.as_tlv_stream();
1154 let invalid_chains = vec![
1155 ChainHash::using_genesis_block(Network::Bitcoin),
1156 ChainHash::using_genesis_block(Network::Testnet),
1158 tlv_stream.0.chains = Some(&invalid_chains);
1159 match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1160 Ok(_) => panic!("expected error"),
1164 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedChain)