12
12
use bitcoin:: blockdata:: constants:: ChainHash ;
13
13
use bitcoin:: secp256k1:: PublicKey ;
14
14
use bitcoin:: secp256k1:: schnorr:: Signature ;
15
+ use core:: convert:: TryFrom ;
16
+ use crate :: io;
15
17
use crate :: ln:: features:: OfferFeatures ;
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 } ;
18
24
19
25
use crate :: prelude:: * ;
20
26
@@ -74,9 +80,9 @@ impl InvoiceRequest {
74
80
self . contents . features . as_ref ( )
75
81
}
76
82
77
- /// The quantity of the offer's item conforming to [`Offer::supported_quantity `].
83
+ /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity `].
78
84
///
79
- /// [`Offer::supported_quantity `]: crate::offers::offer::Offer::supported_quantity
85
+ /// [`Offer::is_valid_quantity `]: crate::offers::offer::Offer::is_valid_quantity
80
86
pub fn quantity ( & self ) -> Option < u64 > {
81
87
self . contents . quantity
82
88
}
@@ -98,3 +104,126 @@ impl InvoiceRequest {
98
104
self . signature
99
105
}
100
106
}
107
+
108
+ impl Writeable for InvoiceRequest {
109
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
110
+ WithoutLength ( & self . bytes ) . write ( writer)
111
+ }
112
+ }
113
+
114
+ impl TryFrom < Vec < u8 > > for InvoiceRequest {
115
+ type Error = ParseError ;
116
+
117
+ fn try_from ( bytes : Vec < u8 > ) -> Result < Self , Self :: Error > {
118
+ let parsed_invoice_request = ParsedMessage :: < FullInvoiceRequestTlvStream > :: try_from ( bytes) ?;
119
+ InvoiceRequest :: try_from ( parsed_invoice_request)
120
+ }
121
+ }
122
+
123
+ tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , 80 ..160 , {
124
+ ( 80 , chain: ChainHash ) ,
125
+ ( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
126
+ ( 84 , features: OfferFeatures ) ,
127
+ ( 86 , quantity: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
128
+ ( 88 , payer_id: PublicKey ) ,
129
+ ( 89 , payer_note: ( String , WithoutLength ) ) ,
130
+ } ) ;
131
+
132
+ type FullInvoiceRequestTlvStream =
133
+ ( PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream , SignatureTlvStream ) ;
134
+
135
+ impl SeekReadable for FullInvoiceRequestTlvStream {
136
+ fn read < R : io:: Read + io:: Seek > ( r : & mut R ) -> Result < Self , DecodeError > {
137
+ let payer = SeekReadable :: read ( r) ?;
138
+ let offer = SeekReadable :: read ( r) ?;
139
+ let invoice_request = SeekReadable :: read ( r) ?;
140
+ let signature = SeekReadable :: read ( r) ?;
141
+
142
+ Ok ( ( payer, offer, invoice_request, signature) )
143
+ }
144
+ }
145
+
146
+ type PartialInvoiceRequestTlvStream = ( PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream ) ;
147
+
148
+ impl TryFrom < ParsedMessage < FullInvoiceRequestTlvStream > > for InvoiceRequest {
149
+ type Error = ParseError ;
150
+
151
+ fn try_from ( invoice_request : ParsedMessage < FullInvoiceRequestTlvStream > )
152
+ -> Result < Self , Self :: Error >
153
+ {
154
+ let ParsedMessage { bytes, tlv_stream } = invoice_request;
155
+ let (
156
+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
157
+ SignatureTlvStream { signature } ,
158
+ ) = tlv_stream;
159
+ let contents = InvoiceRequestContents :: try_from (
160
+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
161
+ ) ?;
162
+
163
+ if let Some ( signature) = & signature {
164
+ let tag = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
165
+ merkle:: verify_signature ( signature, tag, & bytes, contents. payer_id ) ?;
166
+ }
167
+
168
+ Ok ( InvoiceRequest { bytes, contents, signature } )
169
+ }
170
+ }
171
+
172
+ impl TryFrom < PartialInvoiceRequestTlvStream > for InvoiceRequestContents {
173
+ type Error = SemanticError ;
174
+
175
+ fn try_from ( tlv_stream : PartialInvoiceRequestTlvStream ) -> Result < Self , Self :: Error > {
176
+ let (
177
+ PayerTlvStream { metadata } ,
178
+ offer_tlv_stream,
179
+ InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note } ,
180
+ ) = tlv_stream;
181
+
182
+ let payer = PayerContents ( metadata) ;
183
+ let offer = OfferContents :: try_from ( offer_tlv_stream) ?;
184
+
185
+ if !offer. supports_chain ( chain. unwrap_or_else ( || offer. implied_chain ( ) ) ) {
186
+ return Err ( SemanticError :: UnsupportedChain ) ;
187
+ }
188
+
189
+ let amount_msats = match ( offer. amount ( ) , amount) {
190
+ ( Some ( _) , None ) => return Err ( SemanticError :: MissingAmount ) ,
191
+ ( Some ( Amount :: Currency { .. } ) , _) => return Err ( SemanticError :: UnsupportedCurrency ) ,
192
+ ( _, amount_msats) => amount_msats,
193
+ } ;
194
+
195
+ if let Some ( features) = & features {
196
+ if features. requires_unknown_bits ( ) {
197
+ return Err ( SemanticError :: UnknownRequiredFeatures ) ;
198
+ }
199
+ }
200
+
201
+ let expects_quantity = offer. expects_quantity ( ) ;
202
+ let quantity = match quantity {
203
+ None if expects_quantity => return Err ( SemanticError :: MissingQuantity ) ,
204
+ Some ( _) if !expects_quantity => return Err ( SemanticError :: UnexpectedQuantity ) ,
205
+ Some ( quantity) if !offer. is_valid_quantity ( quantity) => {
206
+ return Err ( SemanticError :: InvalidQuantity ) ;
207
+ }
208
+ quantity => quantity,
209
+ } ;
210
+
211
+ {
212
+ let amount_msats = amount_msats. unwrap_or ( offer. amount_msats ( ) ) ;
213
+ let quantity = quantity. unwrap_or ( 1 ) ;
214
+ if amount_msats < offer. expected_invoice_amount_msats ( quantity) {
215
+ return Err ( SemanticError :: InsufficientAmount ) ;
216
+ }
217
+ }
218
+
219
+
220
+ let payer_id = match payer_id {
221
+ None => return Err ( SemanticError :: MissingPayerId ) ,
222
+ Some ( payer_id) => payer_id,
223
+ } ;
224
+
225
+ Ok ( InvoiceRequestContents {
226
+ payer, offer, chain, amount_msats, features, quantity, payer_id, payer_note,
227
+ } )
228
+ }
229
+ }
0 commit comments