8
8
// licenses.
9
9
10
10
//! Data structures and encoding for `invoice_request` messages.
11
+ //!
12
+ //! An [`InvoiceRequest`] can be either built from a parsed [`Offer`] as an "offer to be paid" or
13
+ //! built directly as an "offer for money" (e.g., refund, ATM withdrawal). In the former case, it is
14
+ //! typically constructed by a customer and sent to the merchant who had published the corresponding
15
+ //! offer. In the latter case, an offer doesn't exist as a precursor to the request. Rather the
16
+ //! merchant would typically construct the invoice request and presents it to the customer.
17
+ //!
18
+ //! The recipient of the request responds with an `Invoice`.
19
+ //!
20
+ //! ```ignore
21
+ //! extern crate bitcoin;
22
+ //! extern crate lightning;
23
+ //!
24
+ //! use bitcoin::network::constants::Network;
25
+ //! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
26
+ //! use lightning::ln::features::OfferFeatures;
27
+ //! use lightning::offers::offer::Offer;
28
+ //! use lightning::util::ser::Writeable;
29
+ //!
30
+ //! # fn parse() -> Result<(), lightning::offers::parse::ParseError> {
31
+ //! let secp_ctx = Secp256k1::new();
32
+ //! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
33
+ //! let pubkey = PublicKey::from(keys);
34
+ //! let mut buffer = Vec::new();
35
+ //!
36
+ //! // "offer to be paid" flow
37
+ //! "lno1qcp4256ypq"
38
+ //! .parse::<Offer>()?
39
+ //! .request_invoice(pubkey)
40
+ //! .metadata(vec![42; 64])
41
+ //! .chain(Network::Testnet)
42
+ //! .amount_msats(1000)
43
+ //! .quantity(5)
44
+ //! .payer_note("foo".to_string())
45
+ //! .build()?
46
+ //! .sign(|digest| secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))?
47
+ //! .write(&mut buffer)
48
+ //! .unwrap();
49
+ //! # Ok(())
50
+ //! # }
51
+ //! ```
11
52
12
53
use bitcoin:: blockdata:: constants:: ChainHash ;
13
- use bitcoin:: secp256k1:: PublicKey ;
54
+ use bitcoin:: network:: constants:: Network ;
55
+ use bitcoin:: secp256k1:: { Message , PublicKey , self } ;
14
56
use bitcoin:: secp256k1:: schnorr:: Signature ;
15
57
use core:: convert:: TryFrom ;
16
58
use crate :: io;
17
59
use crate :: ln:: features:: InvoiceRequestFeatures ;
18
60
use crate :: ln:: msgs:: DecodeError ;
19
- use crate :: offers:: merkle:: { SignatureTlvStream , self } ;
20
- use crate :: offers:: offer:: { Amount , OfferContents , OfferTlvStream } ;
61
+ use crate :: offers:: merkle:: { SignatureTlvStream , SignatureTlvStreamRef , self } ;
62
+ use crate :: offers:: offer:: { Amount , Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
21
63
use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
22
- use crate :: offers:: payer:: { PayerContents , PayerTlvStream } ;
64
+ use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
23
65
use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
24
66
use crate :: util:: string:: PrintableString ;
25
67
26
68
use crate :: prelude:: * ;
27
69
70
+ const SIGNATURE_TAG : & ' static str = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
71
+
72
+ /// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow.
73
+ ///
74
+ /// See [module-level documentation] for usage.
75
+ ///
76
+ /// [module-level documentation]: self
77
+ pub struct InvoiceRequestBuilder < ' a > {
78
+ offer : & ' a Offer ,
79
+ invoice_request : InvoiceRequestContents ,
80
+ }
81
+
82
+ impl < ' a > InvoiceRequestBuilder < ' a > {
83
+ pub ( super ) fn new ( offer : & ' a Offer , payer_id : PublicKey ) -> Self {
84
+ Self {
85
+ offer,
86
+ invoice_request : InvoiceRequestContents {
87
+ payer : PayerContents ( None ) , offer : offer. contents . clone ( ) , chain : None ,
88
+ amount_msats : None , features : InvoiceRequestFeatures :: empty ( ) , quantity : None ,
89
+ payer_id, payer_note : None ,
90
+ } ,
91
+ }
92
+ }
93
+
94
+ /// Sets the metadata for the invoice request. Useful for containing information about the
95
+ /// derivation of [`InvoiceRequest::payer_id`]. This should not leak any information such as
96
+ /// using a simple BIP-32 derivation path.
97
+ ///
98
+ /// Successive calls to this method will override the previous setting.
99
+ pub fn metadata ( mut self , metadata : Vec < u8 > ) -> Self {
100
+ self . invoice_request . payer = PayerContents ( Some ( metadata) ) ;
101
+ self
102
+ }
103
+
104
+ /// Sets the chain hash of the given [`Network`] for paying an invoice. If not called,
105
+ /// [`Network::Bitcoin`] is assumed. Must be supported by the offer.
106
+ ///
107
+ /// Successive calls to this method will override the previous setting.
108
+ pub fn chain ( mut self , network : Network ) -> Self {
109
+ self . invoice_request . chain = Some ( ChainHash :: using_genesis_block ( network) ) ;
110
+ self
111
+ }
112
+
113
+ /// Sets the amount for paying an invoice. Must be at least the base invoice amount (i.e.,
114
+ /// [`Offer::amount`] times [`quantity`]).
115
+ ///
116
+ /// Successive calls to this method will override the previous setting.
117
+ ///
118
+ /// [`quantity`]: Self::quantity
119
+ pub fn amount_msats ( mut self , amount_msats : u64 ) -> Self {
120
+ self . invoice_request . amount_msats = Some ( amount_msats) ;
121
+ self
122
+ }
123
+
124
+ /// Sets the features for the invoice request.
125
+ ///
126
+ /// Successive calls to this method will override the previous setting.
127
+ #[ cfg( test) ]
128
+ pub fn features ( mut self , features : InvoiceRequestFeatures ) -> Self {
129
+ self . invoice_request . features = features;
130
+ self
131
+ }
132
+
133
+ /// Sets a quantity of items for the invoice request. If not set, `1` is assumed. Must conform
134
+ /// to [`Offer::is_valid_quantity`].
135
+ ///
136
+ /// Successive calls to this method will override the previous setting.
137
+ pub fn quantity ( mut self , quantity : u64 ) -> Self {
138
+ self . invoice_request . quantity = Some ( quantity) ;
139
+ self
140
+ }
141
+
142
+ /// Sets a note for the invoice request.
143
+ ///
144
+ /// Successive calls to this method will override the previous setting.
145
+ pub fn payer_note ( mut self , payer_note : String ) -> Self {
146
+ self . invoice_request . payer_note = Some ( payer_note) ;
147
+ self
148
+ }
149
+
150
+ /// Builds an [`InvoiceRequest`] after checking for valid semantics.
151
+ pub fn build ( mut self ) -> Result < UnsignedInvoiceRequest < ' a > , SemanticError > {
152
+ let chain = self . invoice_request . chain ( ) ;
153
+ if !self . offer . supports_chain ( chain) {
154
+ return Err ( SemanticError :: UnsupportedChain ) ;
155
+ }
156
+
157
+ if chain == self . offer . implied_chain ( ) {
158
+ self . invoice_request . chain = None ;
159
+ }
160
+
161
+ if self . offer . amount ( ) . is_none ( ) && self . invoice_request . amount_msats . is_none ( ) {
162
+ return Err ( SemanticError :: MissingAmount ) ;
163
+ }
164
+
165
+ if let Some ( Amount :: Currency { .. } ) = self . offer . amount ( ) {
166
+ return Err ( SemanticError :: UnsupportedCurrency ) ;
167
+ }
168
+
169
+ let expects_quantity = self . offer . expects_quantity ( ) ;
170
+ match self . invoice_request . quantity {
171
+ None if expects_quantity => return Err ( SemanticError :: MissingQuantity ) ,
172
+ Some ( _) if !expects_quantity => return Err ( SemanticError :: UnexpectedQuantity ) ,
173
+ Some ( quantity) if !self . offer . is_valid_quantity ( quantity) => {
174
+ return Err ( SemanticError :: InvalidQuantity ) ;
175
+ } ,
176
+ _ => { } ,
177
+ }
178
+
179
+ if self . offer . features ( ) . requires_unknown_bits ( ) {
180
+ return Err ( SemanticError :: UnknownRequiredFeatures ) ;
181
+ }
182
+
183
+ let amount_msats = self . invoice_request . amount_msats . unwrap_or ( self . offer . amount_msats ( ) ) ;
184
+ let quantity = self . invoice_request . quantity . unwrap_or ( 1 ) ;
185
+ if amount_msats < self . offer . expected_invoice_amount_msats ( quantity) {
186
+ return Err ( SemanticError :: InsufficientAmount ) ;
187
+ }
188
+
189
+ let InvoiceRequestBuilder { offer, invoice_request } = self ;
190
+ Ok ( UnsignedInvoiceRequest { offer, invoice_request } )
191
+ }
192
+ }
193
+
194
+ /// A semantically valid [`InvoiceRequest`] that hasn't been signed.
195
+ pub struct UnsignedInvoiceRequest < ' a > {
196
+ offer : & ' a Offer ,
197
+ invoice_request : InvoiceRequestContents ,
198
+ }
199
+
200
+ impl < ' a > UnsignedInvoiceRequest < ' a > {
201
+ /// Signs the invoice request using the given function.
202
+ pub fn sign < F > ( self , sign : F ) -> Result < InvoiceRequest , secp256k1:: Error >
203
+ where F : FnOnce ( & Message ) -> Signature
204
+ {
205
+ // Use the offer bytes instead of the offer TLV stream as the offer may have contained
206
+ // unknown TLV records, which are not stored in `OfferContents`.
207
+ let ( payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) =
208
+ self . invoice_request . as_tlv_stream ( ) ;
209
+ let offer_bytes = WithoutLength ( & self . offer . bytes ) ;
210
+ let unsigned_tlv_stream = ( payer_tlv_stream, offer_bytes, invoice_request_tlv_stream) ;
211
+
212
+ let mut bytes = Vec :: new ( ) ;
213
+ unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
214
+
215
+ let pubkey = self . invoice_request . payer_id ;
216
+ let signature = Some ( merkle:: sign_message ( sign, SIGNATURE_TAG , & bytes, pubkey) ?) ;
217
+
218
+ // Append the signature TLV record to the bytes.
219
+ let signature_tlv_stream = SignatureTlvStreamRef {
220
+ signature : signature. as_ref ( ) ,
221
+ } ;
222
+ signature_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
223
+
224
+ Ok ( InvoiceRequest {
225
+ bytes,
226
+ contents : self . invoice_request ,
227
+ signature,
228
+ } )
229
+ }
230
+ }
231
+
28
232
/// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`].
29
233
///
30
234
/// An offer may provided choices such as quantity, amount, chain, features, etc. An invoice request
@@ -61,17 +265,14 @@ impl InvoiceRequest {
61
265
}
62
266
63
267
/// A chain from [`Offer::chains`] that the offer is valid for.
64
- ///
65
- /// [`Offer::chains`]: crate::offers::offer::Offer::chains
66
268
pub fn chain ( & self ) -> ChainHash {
67
- self . contents . chain . unwrap_or_else ( || self . contents . offer . implied_chain ( ) )
269
+ self . contents . chain ( )
68
270
}
69
271
70
272
/// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
71
273
/// must be greater than or equal to [`Offer::amount`], converted if necessary.
72
274
///
73
275
/// [`chain`]: Self::chain
74
- /// [`Offer::amount`]: crate::offers::offer::Offer::amount
75
276
pub fn amount_msats ( & self ) -> Option < u64 > {
76
277
self . contents . amount_msats
77
278
}
@@ -82,8 +283,6 @@ impl InvoiceRequest {
82
283
}
83
284
84
285
/// 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
286
pub fn quantity ( & self ) -> Option < u64 > {
88
287
self . contents . quantity
89
288
}
@@ -106,12 +305,48 @@ impl InvoiceRequest {
106
305
}
107
306
}
108
307
308
+ impl InvoiceRequestContents {
309
+ fn chain ( & self ) -> ChainHash {
310
+ self . chain . unwrap_or_else ( || self . offer . implied_chain ( ) )
311
+ }
312
+
313
+ pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
314
+ let payer = PayerTlvStreamRef {
315
+ metadata : self . payer . 0 . as_ref ( ) ,
316
+ } ;
317
+
318
+ let offer = self . offer . as_tlv_stream ( ) ;
319
+
320
+ let features = {
321
+ if self . features == InvoiceRequestFeatures :: empty ( ) { None }
322
+ else { Some ( & self . features ) }
323
+ } ;
324
+
325
+ let invoice_request = InvoiceRequestTlvStreamRef {
326
+ chain : self . chain . as_ref ( ) ,
327
+ amount : self . amount_msats ,
328
+ features,
329
+ quantity : self . quantity ,
330
+ payer_id : Some ( & self . payer_id ) ,
331
+ payer_note : self . payer_note . as_ref ( ) ,
332
+ } ;
333
+
334
+ ( payer, offer, invoice_request)
335
+ }
336
+ }
337
+
109
338
impl Writeable for InvoiceRequest {
110
339
fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
111
340
WithoutLength ( & self . bytes ) . write ( writer)
112
341
}
113
342
}
114
343
344
+ impl Writeable for InvoiceRequestContents {
345
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
346
+ self . as_tlv_stream ( ) . write ( writer)
347
+ }
348
+ }
349
+
115
350
tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , 80 ..160 , {
116
351
( 80 , chain: ChainHash ) ,
117
352
( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
@@ -137,6 +372,12 @@ impl SeekReadable for FullInvoiceRequestTlvStream {
137
372
138
373
type PartialInvoiceRequestTlvStream = ( PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream ) ;
139
374
375
+ type PartialInvoiceRequestTlvStreamRef < ' a > = (
376
+ PayerTlvStreamRef < ' a > ,
377
+ OfferTlvStreamRef < ' a > ,
378
+ InvoiceRequestTlvStreamRef < ' a > ,
379
+ ) ;
380
+
140
381
impl TryFrom < Vec < u8 > > for InvoiceRequest {
141
382
type Error = ParseError ;
142
383
@@ -152,8 +393,7 @@ impl TryFrom<Vec<u8>> for InvoiceRequest {
152
393
) ?;
153
394
154
395
if let Some ( signature) = & signature {
155
- let tag = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
156
- merkle:: verify_signature ( signature, tag, & bytes, contents. payer_id ) ?;
396
+ merkle:: verify_signature ( signature, SIGNATURE_TAG , & bytes, contents. payer_id ) ?;
157
397
}
158
398
159
399
Ok ( InvoiceRequest { bytes, contents, signature } )
@@ -213,3 +453,39 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
213
453
} )
214
454
}
215
455
}
456
+
457
+ #[ cfg( test) ]
458
+ mod tests {
459
+ use super :: InvoiceRequest ;
460
+
461
+ use bitcoin:: secp256k1:: { KeyPair , Secp256k1 , SecretKey } ;
462
+ use core:: convert:: TryFrom ;
463
+ use crate :: ln:: msgs:: DecodeError ;
464
+ use crate :: offers:: offer:: OfferBuilder ;
465
+ use crate :: offers:: parse:: ParseError ;
466
+ use crate :: util:: ser:: { BigSize , Writeable } ;
467
+
468
+ #[ test]
469
+ fn fails_parsing_invoice_request_with_extra_tlv_records ( ) {
470
+ let secp_ctx = Secp256k1 :: new ( ) ;
471
+ let keys = KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
472
+ let invoice_request = OfferBuilder :: new ( "foo" . into ( ) , keys. public_key ( ) )
473
+ . amount_msats ( 1000 )
474
+ . build ( )
475
+ . unwrap ( )
476
+ . request_invoice ( keys. public_key ( ) )
477
+ . build ( ) . unwrap ( )
478
+ . sign ( |digest| secp_ctx. sign_schnorr_no_aux_rand ( digest, & keys) ) . unwrap ( ) ;
479
+
480
+ let mut encoded_invoice_request = Vec :: new ( ) ;
481
+ invoice_request. write ( & mut encoded_invoice_request) . unwrap ( ) ;
482
+ BigSize ( 1002 ) . write ( & mut encoded_invoice_request) . unwrap ( ) ;
483
+ BigSize ( 32 ) . write ( & mut encoded_invoice_request) . unwrap ( ) ;
484
+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice_request) . unwrap ( ) ;
485
+
486
+ match InvoiceRequest :: try_from ( encoded_invoice_request) {
487
+ Ok ( _) => panic ! ( "expected error" ) ,
488
+ Err ( e) => assert_eq ! ( e, ParseError :: Decode ( DecodeError :: InvalidValue ) ) ,
489
+ }
490
+ }
491
+ }
0 commit comments