]> git.bitcoin.ninja Git - rust-lightning/blob - lightning/src/offers/invoice_request.rs
6cedf3eda59d68b2d18ac8aaa3bde05aabfb10b6
[rust-lightning] / lightning / src / offers / invoice_request.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 `invoice_request` messages.
11
12 use bitcoin::blockdata::constants::ChainHash;
13 use bitcoin::secp256k1::PublicKey;
14 use bitcoin::secp256k1::schnorr::Signature;
15 use core::convert::TryFrom;
16 use crate::io;
17 use crate::ln::features::InvoiceRequestFeatures;
18 use crate::ln::msgs::DecodeError;
19 use crate::offers::merkle::{SignatureTlvStream, self};
20 use crate::offers::offer::{Amount, OfferContents, OfferTlvStream};
21 use crate::offers::parse::{ParseError, ParsedMessage, SemanticError};
22 use crate::offers::payer::{PayerContents, PayerTlvStream};
23 use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
24 use crate::util::string::PrintableString;
25
26 use crate::prelude::*;
27
28 /// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`].
29 ///
30 /// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request
31 /// specifies these such that its recipient can send an invoice for payment.
32 ///
33 /// [`Offer`]: crate::offers::offer::Offer
34 #[derive(Clone, Debug)]
35 pub struct InvoiceRequest {
36         bytes: Vec<u8>,
37         contents: InvoiceRequestContents,
38         signature: Option<Signature>,
39 }
40
41 /// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
42 #[derive(Clone, Debug)]
43 pub(super) struct InvoiceRequestContents {
44         payer: PayerContents,
45         offer: OfferContents,
46         chain: Option<ChainHash>,
47         amount_msats: Option<u64>,
48         features: InvoiceRequestFeatures,
49         quantity: Option<u64>,
50         payer_id: PublicKey,
51         payer_note: Option<String>,
52 }
53
54 impl InvoiceRequest {
55         /// An unpredictable series of bytes, typically containing information about the derivation of
56         /// [`payer_id`].
57         ///
58         /// [`payer_id`]: Self::payer_id
59         pub fn metadata(&self) -> &[u8] {
60                 &self.contents.payer.0[..]
61         }
62
63         /// A chain from [`Offer::chains`] that the offer is valid for.
64         ///
65         /// [`Offer::chains`]: crate::offers::offer::Offer::chains
66         pub fn chain(&self) -> ChainHash {
67                 self.contents.chain.unwrap_or_else(|| self.contents.offer.implied_chain())
68         }
69
70         /// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
71         /// must be greater than or equal to [`Offer::amount`], converted if necessary.
72         ///
73         /// [`chain`]: Self::chain
74         /// [`Offer::amount`]: crate::offers::offer::Offer::amount
75         pub fn amount_msats(&self) -> Option<u64> {
76                 self.contents.amount_msats
77         }
78
79         /// Features for paying the invoice.
80         pub fn features(&self) -> &InvoiceRequestFeatures {
81                 &self.contents.features
82         }
83
84         /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
85         ///
86         /// [`Offer::is_valid_quantity`]: crate::offers::offer::Offer::is_valid_quantity
87         pub fn quantity(&self) -> Option<u64> {
88                 self.contents.quantity
89         }
90
91         /// A possibly transient pubkey used to sign the invoice request.
92         pub fn payer_id(&self) -> PublicKey {
93                 self.contents.payer_id
94         }
95
96         /// Payer provided note to include in the invoice.
97         pub fn payer_note(&self) -> Option<PrintableString> {
98                 self.contents.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
99         }
100
101         /// Signature of the invoice request using [`payer_id`].
102         ///
103         /// [`payer_id`]: Self::payer_id
104         pub fn signature(&self) -> Option<Signature> {
105                 self.signature
106         }
107 }
108
109 impl Writeable for InvoiceRequest {
110         fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
111                 WithoutLength(&self.bytes).write(writer)
112         }
113 }
114
115 tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
116         (80, chain: ChainHash),
117         (82, amount: (u64, HighZeroBytesDroppedBigSize)),
118         (84, features: InvoiceRequestFeatures),
119         (86, quantity: (u64, HighZeroBytesDroppedBigSize)),
120         (88, payer_id: PublicKey),
121         (89, payer_note: (String, WithoutLength)),
122 });
123
124 type FullInvoiceRequestTlvStream =
125         (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream);
126
127 impl SeekReadable for FullInvoiceRequestTlvStream {
128         fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
129                 let payer = SeekReadable::read(r)?;
130                 let offer = SeekReadable::read(r)?;
131                 let invoice_request = SeekReadable::read(r)?;
132                 let signature = SeekReadable::read(r)?;
133
134                 Ok((payer, offer, invoice_request, signature))
135         }
136 }
137
138 type PartialInvoiceRequestTlvStream = (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream);
139
140 impl TryFrom<Vec<u8>> for InvoiceRequest {
141         type Error = ParseError;
142
143         fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
144                 let invoice_request = ParsedMessage::<FullInvoiceRequestTlvStream>::try_from(bytes)?;
145                 let ParsedMessage { bytes, tlv_stream } = invoice_request;
146                 let (
147                         payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
148                         SignatureTlvStream { signature },
149                 ) = tlv_stream;
150                 let contents = InvoiceRequestContents::try_from(
151                         (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
152                 )?;
153
154                 if let Some(signature) = &signature {
155                         let tag = concat!("lightning", "invoice_request", "signature");
156                         merkle::verify_signature(signature, tag, &bytes, contents.payer_id)?;
157                 }
158
159                 Ok(InvoiceRequest { bytes, contents, signature })
160         }
161 }
162
163 impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
164         type Error = SemanticError;
165
166         fn try_from(tlv_stream: PartialInvoiceRequestTlvStream) -> Result<Self, Self::Error> {
167                 let (
168                         PayerTlvStream { metadata },
169                         offer_tlv_stream,
170                         InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
171                 ) = tlv_stream;
172
173                 let payer = match metadata {
174                         None => return Err(SemanticError::MissingPayerMetadata),
175                         Some(metadata) => PayerContents(metadata),
176                 };
177                 let offer = OfferContents::try_from(offer_tlv_stream)?;
178
179                 if !offer.supports_chain(chain.unwrap_or_else(|| offer.implied_chain())) {
180                         return Err(SemanticError::UnsupportedChain);
181                 }
182
183                 let amount_msats = match (offer.amount(), amount) {
184                         (None, None) => return Err(SemanticError::MissingAmount),
185                         (Some(Amount::Currency { .. }), _) => return Err(SemanticError::UnsupportedCurrency),
186                         (_, amount_msats) => amount_msats,
187                 };
188
189                 let features = features.unwrap_or_else(InvoiceRequestFeatures::empty);
190
191                 let expects_quantity = offer.expects_quantity();
192                 let quantity = match quantity {
193                         None if expects_quantity => return Err(SemanticError::MissingQuantity),
194                         Some(_) if !expects_quantity => return Err(SemanticError::UnexpectedQuantity),
195                         Some(quantity) if !offer.is_valid_quantity(quantity) => {
196                                 return Err(SemanticError::InvalidQuantity);
197                         }
198                         quantity => quantity,
199                 };
200
201                 {
202                         let amount_msats = amount_msats.unwrap_or(offer.amount_msats());
203                         let quantity = quantity.unwrap_or(1);
204                         if amount_msats < offer.expected_invoice_amount_msats(quantity) {
205                                 return Err(SemanticError::InsufficientAmount);
206                         }
207                 }
208
209                 let payer_id = match payer_id {
210                         None => return Err(SemanticError::MissingPayerId),
211                         Some(payer_id) => payer_id,
212                 };
213
214                 Ok(InvoiceRequestContents {
215                         payer, offer, chain, amount_msats, features, quantity, payer_id, payer_note,
216                 })
217         }
218 }