]> git.bitcoin.ninja Git - rust-lightning/blob - lightning/src/offers/static_invoice.rs
0cf5e92107c923138063e0795c88e954afd614a6
[rust-lightning] / lightning / src / offers / static_invoice.rs
1 // This file is Copyright its original authors, visible in version control
2 // history.
3 //
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
8 // licenses.
9
10 //! Data structures and encoding for static BOLT 12 invoices.
11
12 use crate::blinded_path::BlindedPath;
13 use crate::io;
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,
19 };
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,
26 };
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;
33
34 #[cfg(feature = "std")]
35 use crate::offers::invoice::is_expired;
36
37 #[allow(unused_imports)]
38 use crate::prelude::*;
39
40 /// Static invoices default to expiring after 2 weeks.
41 const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(3600 * 24 * 14);
42
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");
45
46 /// A `StaticInvoice` is a reusable payment request corresponding to an [`Offer`].
47 ///
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.
52 ///
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 {
58         bytes: Vec<u8>,
59         contents: InvoiceContents,
60         signature: Signature,
61 }
62
63 /// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
64 ///
65 /// [`Offer`]: crate::offers::offer::Offer
66 #[derive(Clone, Debug)]
67 struct InvoiceContents {
68         offer: OfferContents,
69         payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
70         created_at: Duration,
71         relative_expiry: Option<Duration>,
72         fallbacks: Option<Vec<FallbackAddress>>,
73         features: Bolt12InvoiceFeatures,
74         signing_pubkey: PublicKey,
75         message_paths: Vec<BlindedPath>,
76 }
77
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 {
82                 $contents.chain()
83         }
84
85         /// Opaque bytes set by the originating [`Offer::metadata`].
86         ///
87         /// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
88         pub fn metadata(&$self) -> Option<&Vec<u8>> {
89                 $contents.metadata()
90         }
91
92         /// The minimum amount required for a successful payment of a single item.
93         ///
94         /// From [`Offer::amount`].
95         ///
96         /// [`Offer::amount`]: crate::offers::offer::Offer::amount
97         pub fn amount(&$self) -> Option<Amount> {
98                 $contents.amount()
99         }
100
101         /// Features pertaining to the originating [`Offer`], from [`Offer::offer_features`].
102         ///
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()
107         }
108
109         /// A complete description of the purpose of the originating offer, from [`Offer::description`].
110         ///
111         /// [`Offer::description`]: crate::offers::offer::Offer::description
112         pub fn description(&$self) -> Option<PrintableString> {
113                 $contents.description()
114         }
115
116         /// Duration since the Unix epoch when an invoice should no longer be requested, from
117         /// [`Offer::absolute_expiry`].
118         ///
119         /// [`Offer::absolute_expiry`]: crate::offers::offer::Offer::absolute_expiry
120         pub fn absolute_expiry(&$self) -> Option<Duration> {
121                 $contents.absolute_expiry()
122         }
123
124         /// The issuer of the offer, from [`Offer::issuer`].
125         ///
126         /// [`Offer::issuer`]: crate::offers::offer::Offer::issuer
127         pub fn issuer(&$self) -> Option<PrintableString> {
128                 $contents.issuer()
129         }
130
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`].
133         ///
134         /// [`Offer::paths`]: crate::offers::offer::Offer::paths
135         pub fn offer_message_paths(&$self) -> &[BlindedPath] {
136                 $contents.offer_message_paths()
137         }
138
139         /// Paths to the recipient for indicating that a held HTLC is available to claim when they next
140         /// come online.
141         pub fn message_paths(&$self) -> &[BlindedPath] {
142                 $contents.message_paths()
143         }
144
145         /// The quantity of items supported, from [`Offer::supported_quantity`].
146         ///
147         /// [`Offer::supported_quantity`]: crate::offers::offer::Offer::supported_quantity
148         pub fn supported_quantity(&$self) -> Quantity {
149                 $contents.supported_quantity()
150         }
151 } }
152
153 impl StaticInvoice {
154         invoice_accessors_common!(self, self.contents, StaticInvoice);
155         invoice_accessors!(self, self.contents);
156
157         /// Signature of the invoice verified using [`StaticInvoice::signing_pubkey`].
158         pub fn signature(&self) -> Signature {
159                 self.signature
160         }
161 }
162
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())
167         }
168
169         fn metadata(&self) -> Option<&Vec<u8>> {
170                 self.offer.metadata()
171         }
172
173         fn amount(&self) -> Option<Amount> {
174                 self.offer.amount()
175         }
176
177         fn offer_features(&self) -> &OfferFeatures {
178                 self.offer.features()
179         }
180
181         fn description(&self) -> Option<PrintableString> {
182                 self.offer.description()
183         }
184
185         fn absolute_expiry(&self) -> Option<Duration> {
186                 self.offer.absolute_expiry()
187         }
188
189         fn issuer(&self) -> Option<PrintableString> {
190                 self.offer.issuer()
191         }
192
193         fn offer_message_paths(&self) -> &[BlindedPath] {
194                 self.offer.paths()
195         }
196
197         fn message_paths(&self) -> &[BlindedPath] {
198                 &self.message_paths[..]
199         }
200
201         fn supported_quantity(&self) -> Quantity {
202                 self.offer.supported_quantity()
203         }
204
205         fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] {
206                 &self.payment_paths[..]
207         }
208
209         fn created_at(&self) -> Duration {
210                 self.created_at
211         }
212
213         fn relative_expiry(&self) -> Duration {
214                 self.relative_expiry.unwrap_or(DEFAULT_RELATIVE_EXPIRY)
215         }
216
217         #[cfg(feature = "std")]
218         fn is_expired(&self) -> bool {
219                 is_expired(self.created_at(), self.relative_expiry())
220         }
221
222         fn fallbacks(&self) -> Vec<Address> {
223                 let chain = self.chain();
224                 self.fallbacks
225                         .as_ref()
226                         .map(|fallbacks| filter_fallbacks(chain, fallbacks))
227                         .unwrap_or_else(Vec::new)
228         }
229
230         fn features(&self) -> &Bolt12InvoiceFeatures {
231                 &self.features
232         }
233
234         fn signing_pubkey(&self) -> PublicKey {
235                 self.signing_pubkey
236         }
237 }
238
239 impl Writeable for StaticInvoice {
240         fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
241                 WithoutLength(&self.bytes).write(writer)
242         }
243 }
244
245 impl TryFrom<Vec<u8>> for StaticInvoice {
246         type Error = Bolt12ParseError;
247
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)
251         }
252 }
253
254 type FullInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream);
255
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)?;
261
262                 Ok((offer, invoice, signature))
263         }
264 }
265
266 type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream);
267
268 impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
269         type Error = Bolt12ParseError;
270
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))?;
275
276                 let signature = match signature {
277                         None => {
278                                 return Err(Bolt12ParseError::InvalidSemantics(
279                                         Bolt12SemanticError::MissingSignature,
280                                 ))
281                         },
282                         Some(signature) => signature,
283                 };
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)?;
287
288                 Ok(StaticInvoice { bytes, contents, signature })
289         }
290 }
291
292 impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
293         type Error = Bolt12SemanticError;
294
295         fn try_from(tlv_stream: PartialInvoiceTlvStream) -> Result<Self, Self::Error> {
296                 let (
297                         offer_tlv_stream,
298                         InvoiceTlvStream {
299                                 paths,
300                                 blindedpay,
301                                 created_at,
302                                 relative_expiry,
303                                 fallbacks,
304                                 features,
305                                 node_id,
306                                 message_paths,
307                                 payment_hash,
308                                 amount,
309                         },
310                 ) = tlv_stream;
311
312                 if payment_hash.is_some() {
313                         return Err(Bolt12SemanticError::UnexpectedPaymentHash);
314                 }
315                 if amount.is_some() {
316                         return Err(Bolt12SemanticError::UnexpectedAmount);
317                 }
318
319                 let payment_paths = construct_payment_paths(blindedpay, paths)?;
320                 let message_paths = message_paths.ok_or(Bolt12SemanticError::MissingPaths)?;
321
322                 let created_at = match created_at {
323                         None => return Err(Bolt12SemanticError::MissingCreationTime),
324                         Some(timestamp) => Duration::from_secs(timestamp),
325                 };
326
327                 let relative_expiry = relative_expiry.map(Into::<u64>::into).map(Duration::from_secs);
328
329                 let features = features.unwrap_or_else(Bolt12InvoiceFeatures::empty);
330
331                 let signing_pubkey = node_id.ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
332                 check_invoice_signing_pubkey(&signing_pubkey, &offer_tlv_stream)?;
333
334                 if offer_tlv_stream.paths.is_none() {
335                         return Err(Bolt12SemanticError::MissingPaths);
336                 }
337                 if offer_tlv_stream.chains.as_ref().map_or(0, |chains| chains.len()) > 1 {
338                         return Err(Bolt12SemanticError::UnexpectedChain);
339                 }
340
341                 Ok(InvoiceContents {
342                         offer: OfferContents::try_from(offer_tlv_stream)?,
343                         payment_paths,
344                         message_paths,
345                         created_at,
346                         relative_expiry,
347                         fallbacks,
348                         features,
349                         signing_pubkey,
350                 })
351         }
352 }