1212use bitcoin:: blockdata:: constants:: ChainHash ;
1313use bitcoin:: secp256k1:: PublicKey ;
1414use bitcoin:: secp256k1:: schnorr:: Signature ;
15+ use core:: convert:: TryFrom ;
16+ use crate :: io;
1517use crate :: ln:: features:: InvoiceRequestFeatures ;
16- use crate :: offers:: offer:: OfferContents ;
17- use crate :: offers:: payer:: PayerContents ;
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 } ;
1824use crate :: util:: string:: PrintableString ;
1925
2026use crate :: prelude:: * ;
@@ -34,7 +40,7 @@ pub struct InvoiceRequest {
3440
3541/// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
3642#[ derive( Clone , Debug ) ]
37- pub ( crate ) struct InvoiceRequestContents {
43+ pub ( super ) struct InvoiceRequestContents {
3844 payer : PayerContents ,
3945 offer : OfferContents ,
4046 chain : Option < ChainHash > ,
@@ -75,9 +81,9 @@ impl InvoiceRequest {
7581 & self . contents . features
7682 }
7783
78- /// The quantity of the offer's item conforming to [`Offer::supported_quantity `].
84+ /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity `].
7985 ///
80- /// [`Offer::supported_quantity `]: crate::offers::offer::Offer::supported_quantity
86+ /// [`Offer::is_valid_quantity `]: crate::offers::offer::Offer::is_valid_quantity
8187 pub fn quantity ( & self ) -> Option < u64 > {
8288 self . contents . quantity
8389 }
@@ -99,3 +105,114 @@ impl InvoiceRequest {
99105 self . signature
100106 }
101107}
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+ }
0 commit comments