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::msgs::DecodeError;
16 use crate::offers::invoice::{
17 check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPathIter,
18 BlindedPayInfo, BlindedPayInfoIter, FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef,
20 use crate::offers::invoice_macros::invoice_accessors_common;
21 use crate::offers::merkle::{self, SignatureTlvStream, TaggedHash};
22 use crate::offers::offer::{Amount, OfferContents, OfferTlvStream, Quantity};
23 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
24 use crate::util::ser::{
25 HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer,
27 use crate::util::string::PrintableString;
28 use bitcoin::address::Address;
29 use bitcoin::blockdata::constants::ChainHash;
30 use bitcoin::secp256k1::schnorr::Signature;
31 use bitcoin::secp256k1::PublicKey;
32 use core::time::Duration;
34 #[cfg(feature = "std")]
35 use crate::offers::invoice::is_expired;
37 #[allow(unused_imports)]
38 use crate::prelude::*;
40 /// Static invoices default to expiring after 2 weeks.
41 const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(3600 * 24 * 14);
43 /// Tag for the hash function used when signing a [`StaticInvoice`]'s merkle root.
44 pub const SIGNATURE_TAG: &'static str = concat!("lightning", "static_invoice", "signature");
46 /// A `StaticInvoice` is a reusable payment request corresponding to an [`Offer`].
48 /// A static invoice may be sent in response to an [`InvoiceRequest`] and includes all the
49 /// information needed to pay the recipient. However, unlike [`Bolt12Invoice`]s, static invoices do
50 /// not provide proof-of-payment. Therefore, [`Bolt12Invoice`]s should be preferred when the
51 /// recipient is online to provide one.
53 /// [`Offer`]: crate::offers::offer::Offer
54 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
55 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
56 #[derive(Clone, Debug)]
57 pub struct StaticInvoice {
59 contents: InvoiceContents,
63 /// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
65 /// [`Offer`]: crate::offers::offer::Offer
66 #[derive(Clone, Debug)]
67 struct InvoiceContents {
69 payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
71 relative_expiry: Option<Duration>,
72 fallbacks: Option<Vec<FallbackAddress>>,
73 features: Bolt12InvoiceFeatures,
74 signing_pubkey: PublicKey,
75 message_paths: Vec<BlindedPath>,
78 macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
79 /// The chain that must be used when paying the invoice. [`StaticInvoice`]s currently can only be
80 /// created from offers that support a single chain.
81 pub fn chain(&$self) -> ChainHash {
85 /// Opaque bytes set by the originating [`Offer::metadata`].
87 /// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
88 pub fn metadata(&$self) -> Option<&Vec<u8>> {
92 /// The minimum amount required for a successful payment of a single item.
94 /// From [`Offer::amount`].
96 /// [`Offer::amount`]: crate::offers::offer::Offer::amount
97 pub fn amount(&$self) -> Option<Amount> {
101 /// Features pertaining to the originating [`Offer`], from [`Offer::offer_features`].
103 /// [`Offer`]: crate::offers::offer::Offer
104 /// [`Offer::offer_features`]: crate::offers::offer::Offer::offer_features
105 pub fn offer_features(&$self) -> &OfferFeatures {
106 $contents.offer_features()
109 /// A complete description of the purpose of the originating offer, from [`Offer::description`].
111 /// [`Offer::description`]: crate::offers::offer::Offer::description
112 pub fn description(&$self) -> Option<PrintableString> {
113 $contents.description()
116 /// Duration since the Unix epoch when an invoice should no longer be requested, from
117 /// [`Offer::absolute_expiry`].
119 /// [`Offer::absolute_expiry`]: crate::offers::offer::Offer::absolute_expiry
120 pub fn absolute_expiry(&$self) -> Option<Duration> {
121 $contents.absolute_expiry()
124 /// The issuer of the offer, from [`Offer::issuer`].
126 /// [`Offer::issuer`]: crate::offers::offer::Offer::issuer
127 pub fn issuer(&$self) -> Option<PrintableString> {
131 /// Paths to the node that may supply the invoice on the recipient's behalf, originating from
132 /// publicly reachable nodes. Taken from [`Offer::paths`].
134 /// [`Offer::paths`]: crate::offers::offer::Offer::paths
135 pub fn offer_message_paths(&$self) -> &[BlindedPath] {
136 $contents.offer_message_paths()
139 /// Paths to the recipient for indicating that a held HTLC is available to claim when they next
141 pub fn message_paths(&$self) -> &[BlindedPath] {
142 $contents.message_paths()
145 /// The quantity of items supported, from [`Offer::supported_quantity`].
147 /// [`Offer::supported_quantity`]: crate::offers::offer::Offer::supported_quantity
148 pub fn supported_quantity(&$self) -> Quantity {
149 $contents.supported_quantity()
154 invoice_accessors_common!(self, self.contents, StaticInvoice);
155 invoice_accessors!(self, self.contents);
157 /// Signature of the invoice verified using [`StaticInvoice::signing_pubkey`].
158 pub fn signature(&self) -> Signature {
163 impl InvoiceContents {
164 fn chain(&self) -> ChainHash {
165 debug_assert_eq!(self.offer.chains().len(), 1);
166 self.offer.chains().first().cloned().unwrap_or_else(|| self.offer.implied_chain())
169 fn metadata(&self) -> Option<&Vec<u8>> {
170 self.offer.metadata()
173 fn amount(&self) -> Option<Amount> {
177 fn offer_features(&self) -> &OfferFeatures {
178 self.offer.features()
181 fn description(&self) -> Option<PrintableString> {
182 self.offer.description()
185 fn absolute_expiry(&self) -> Option<Duration> {
186 self.offer.absolute_expiry()
189 fn issuer(&self) -> Option<PrintableString> {
193 fn offer_message_paths(&self) -> &[BlindedPath] {
197 fn message_paths(&self) -> &[BlindedPath] {
198 &self.message_paths[..]
201 fn supported_quantity(&self) -> Quantity {
202 self.offer.supported_quantity()
205 fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] {
206 &self.payment_paths[..]
209 fn created_at(&self) -> Duration {
213 fn relative_expiry(&self) -> Duration {
214 self.relative_expiry.unwrap_or(DEFAULT_RELATIVE_EXPIRY)
217 #[cfg(feature = "std")]
218 fn is_expired(&self) -> bool {
219 is_expired(self.created_at(), self.relative_expiry())
222 fn fallbacks(&self) -> Vec<Address> {
223 let chain = self.chain();
226 .map(|fallbacks| filter_fallbacks(chain, fallbacks))
227 .unwrap_or_else(Vec::new)
230 fn features(&self) -> &Bolt12InvoiceFeatures {
234 fn signing_pubkey(&self) -> PublicKey {
239 impl Writeable for StaticInvoice {
240 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
241 WithoutLength(&self.bytes).write(writer)
245 impl TryFrom<Vec<u8>> for StaticInvoice {
246 type Error = Bolt12ParseError;
248 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
249 let parsed_invoice = ParsedMessage::<FullInvoiceTlvStream>::try_from(bytes)?;
250 StaticInvoice::try_from(parsed_invoice)
254 type FullInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream);
256 impl SeekReadable for FullInvoiceTlvStream {
257 fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
258 let offer = SeekReadable::read(r)?;
259 let invoice = SeekReadable::read(r)?;
260 let signature = SeekReadable::read(r)?;
262 Ok((offer, invoice, signature))
266 type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream);
268 impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
269 type Error = Bolt12ParseError;
271 fn try_from(invoice: ParsedMessage<FullInvoiceTlvStream>) -> Result<Self, Self::Error> {
272 let ParsedMessage { bytes, tlv_stream } = invoice;
273 let (offer_tlv_stream, invoice_tlv_stream, SignatureTlvStream { signature }) = tlv_stream;
274 let contents = InvoiceContents::try_from((offer_tlv_stream, invoice_tlv_stream))?;
276 let signature = match signature {
278 return Err(Bolt12ParseError::InvalidSemantics(
279 Bolt12SemanticError::MissingSignature,
282 Some(signature) => signature,
284 let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
285 let pubkey = contents.signing_pubkey;
286 merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
288 Ok(StaticInvoice { bytes, contents, signature })
292 impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
293 type Error = Bolt12SemanticError;
295 fn try_from(tlv_stream: PartialInvoiceTlvStream) -> Result<Self, Self::Error> {
312 if payment_hash.is_some() {
313 return Err(Bolt12SemanticError::UnexpectedPaymentHash);
315 if amount.is_some() {
316 return Err(Bolt12SemanticError::UnexpectedAmount);
319 let payment_paths = construct_payment_paths(blindedpay, paths)?;
320 let message_paths = message_paths.ok_or(Bolt12SemanticError::MissingPaths)?;
322 let created_at = match created_at {
323 None => return Err(Bolt12SemanticError::MissingCreationTime),
324 Some(timestamp) => Duration::from_secs(timestamp),
327 let relative_expiry = relative_expiry.map(Into::<u64>::into).map(Duration::from_secs);
329 let features = features.unwrap_or_else(Bolt12InvoiceFeatures::empty);
331 let signing_pubkey = node_id.ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
332 check_invoice_signing_pubkey(&signing_pubkey, &offer_tlv_stream)?;
334 if offer_tlv_stream.paths.is_none() {
335 return Err(Bolt12SemanticError::MissingPaths);
337 if offer_tlv_stream.chains.as_ref().map_or(0, |chains| chains.len()) > 1 {
338 return Err(Bolt12SemanticError::UnexpectedChain);
342 offer: OfferContents::try_from(offer_tlv_stream)?,