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 `invoice_request` messages.
12 use bitcoin::blockdata::constants::ChainHash;
13 use bitcoin::secp256k1::PublicKey;
14 use bitcoin::secp256k1::schnorr::Signature;
15 use core::convert::TryFrom;
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;
26 use crate::prelude::*;
28 /// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`].
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.
33 /// [`Offer`]: crate::offers::offer::Offer
34 #[derive(Clone, Debug)]
35 pub struct InvoiceRequest {
37 contents: InvoiceRequestContents,
38 signature: Option<Signature>,
41 /// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
42 #[derive(Clone, Debug)]
43 pub(super) struct InvoiceRequestContents {
46 chain: Option<ChainHash>,
47 amount_msats: Option<u64>,
48 features: InvoiceRequestFeatures,
49 quantity: Option<u64>,
51 payer_note: Option<String>,
55 /// An unpredictable series of bytes, typically containing information about the derivation of
58 /// [`payer_id`]: Self::payer_id
59 pub fn metadata(&self) -> &[u8] {
60 &self.contents.payer.0[..]
63 /// A chain from [`Offer::chains`] that the offer is valid for.
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())
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.
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
79 /// Features for paying the invoice.
80 pub fn features(&self) -> &InvoiceRequestFeatures {
81 &self.contents.features
84 /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
86 /// [`Offer::is_valid_quantity`]: crate::offers::offer::Offer::is_valid_quantity
87 pub fn quantity(&self) -> Option<u64> {
88 self.contents.quantity
91 /// A possibly transient pubkey used to sign the invoice request.
92 pub fn payer_id(&self) -> PublicKey {
93 self.contents.payer_id
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()))
101 /// Signature of the invoice request using [`payer_id`].
103 /// [`payer_id`]: Self::payer_id
104 pub fn signature(&self) -> Option<Signature> {
109 impl Writeable for InvoiceRequest {
110 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
111 WithoutLength(&self.bytes).write(writer)
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)),
124 type FullInvoiceRequestTlvStream =
125 (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream);
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)?;
134 Ok((payer, offer, invoice_request, signature))
138 type PartialInvoiceRequestTlvStream = (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream);
140 impl TryFrom<Vec<u8>> for InvoiceRequest {
141 type Error = ParseError;
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;
147 payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
148 SignatureTlvStream { signature },
150 let contents = InvoiceRequestContents::try_from(
151 (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
154 if let Some(signature) = &signature {
155 let tag = concat!("lightning", "invoice_request", "signature");
156 merkle::verify_signature(signature, tag, &bytes, contents.payer_id)?;
159 Ok(InvoiceRequest { bytes, contents, signature })
163 impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
164 type Error = SemanticError;
166 fn try_from(tlv_stream: PartialInvoiceRequestTlvStream) -> Result<Self, Self::Error> {
168 PayerTlvStream { metadata },
170 InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
173 let payer = match metadata {
174 None => return Err(SemanticError::MissingPayerMetadata),
175 Some(metadata) => PayerContents(metadata),
177 let offer = OfferContents::try_from(offer_tlv_stream)?;
179 if !offer.supports_chain(chain.unwrap_or_else(|| offer.implied_chain())) {
180 return Err(SemanticError::UnsupportedChain);
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,
189 let features = features.unwrap_or_else(InvoiceRequestFeatures::empty);
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);
198 quantity => quantity,
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);
209 let payer_id = match payer_id {
210 None => return Err(SemanticError::MissingPayerId),
211 Some(payer_id) => payer_id,
214 Ok(InvoiceRequestContents {
215 payer, offer, chain, amount_msats, features, quantity, payer_id, payer_note,