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`] for the user-pays-merchant
13
+ //! flow or built directly for the merchant-pays-user flow.
14
+ //!
15
+ //! ```
16
+ //! extern crate bitcoin;
17
+ //! extern crate lightning;
18
+ //!
19
+ //! use bitcoin::network::constants::Network;
20
+ //! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
21
+ //! use lightning::ln::features::OfferFeatures;
22
+ //! use lightning::offers::Offer;
23
+ //! use lightning::util::ser::Writeable;
24
+ //!
25
+ //! # fn parse() -> Result<(), lightning::offers::ParseError> {
26
+ //! let secp_ctx = Secp256k1::new();
27
+ //! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
28
+ //! let pubkey = PublicKey::from(keys);
29
+ //! let mut buffer = Vec::new();
30
+ //!
31
+ //! // User-pays-merchant flow
32
+ //! "lno1qcp4256ypq"
33
+ //! .parse::<Offer>()?
34
+ //! .request_invoice(pubkey)
35
+ //! .payer_info(vec![42; 64])
36
+ //! .chain(Network::Testnet)?
37
+ //! .amount_msats(1000)?
38
+ //! .features(OfferFeatures::known())
39
+ //! .quantity(5)?
40
+ //! .payer_note("foo".to_string())
41
+ //! .build()?
42
+ //! .sign(|digest| secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))?
43
+ //! .write(&mut buffer)
44
+ //! .unwrap();
45
+ //! # Ok(())
46
+ //! # }
47
+ //! ```
11
48
12
49
use bitcoin:: blockdata:: constants:: ChainHash ;
13
- use bitcoin:: secp256k1:: PublicKey ;
50
+ use bitcoin:: network:: constants:: Network ;
51
+ use bitcoin:: secp256k1:: { Message , PublicKey , self } ;
14
52
use bitcoin:: secp256k1:: schnorr:: Signature ;
15
53
use core:: convert:: TryFrom ;
16
54
use core:: str:: FromStr ;
17
55
use io;
18
56
use ln:: features:: OfferFeatures ;
19
57
use offers:: merkle:: { SignatureTlvStream , self } ;
20
- use offers:: offer:: { Amount , OfferContents , OfferTlvStream , self } ;
58
+ use offers:: offer:: { Offer , OfferContents , OfferTlvStream , self } ;
21
59
use offers:: parse:: { Bech32Encode , ParseError , SemanticError } ;
22
60
use offers:: payer:: { PayerContents , PayerTlvStream , self } ;
23
61
use util:: ser:: { Readable , WithoutLength , Writeable , Writer } ;
24
62
63
+ ///
64
+ const SIGNATURE_TAG : & ' static str = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
65
+
66
+ /// Builds an [`InvoiceRequest`] from an [`Offer`] for the user-pays-merchant flow.
67
+ ///
68
+ /// See [module-level documentation] for usage.
69
+ ///
70
+ /// [module-level documentation]: self
71
+ pub struct InvoiceRequestBuilder < ' a > {
72
+ offer : & ' a Offer ,
73
+ invoice_request : InvoiceRequestContents ,
74
+ }
75
+
76
+ impl < ' a > InvoiceRequestBuilder < ' a > {
77
+ pub ( super ) fn new ( offer : & ' a Offer , payer_id : PublicKey ) -> Self {
78
+ Self {
79
+ offer,
80
+ invoice_request : InvoiceRequestContents {
81
+ payer : PayerContents ( None ) , offer : offer. contents . clone ( ) , chain : None ,
82
+ amount_msats : None , features : None , quantity : None , payer_id, payer_note : None ,
83
+ } ,
84
+ }
85
+ }
86
+
87
+ ///
88
+ pub fn payer_info ( mut self , payer_info : Vec < u8 > ) -> Self {
89
+ self . invoice_request . payer = PayerContents ( Some ( payer_info) ) ;
90
+ self
91
+ }
92
+
93
+ ///
94
+ pub fn chain ( mut self , network : Network ) -> Result < Self , SemanticError > {
95
+ let chain_hash = ChainHash :: using_genesis_block ( network) ;
96
+ if !self . offer . supports_chain ( chain_hash) {
97
+ return Err ( SemanticError :: UnsupportedChain )
98
+ }
99
+
100
+ self . invoice_request . chain = Some ( chain_hash) ;
101
+ Ok ( self )
102
+ }
103
+
104
+ ///
105
+ pub fn amount_msats ( mut self , amount_msats : u64 ) -> Result < Self , SemanticError > {
106
+ if !self . offer . is_sufficient_amount ( amount_msats) {
107
+ return Err ( SemanticError :: InsufficientAmount ) ;
108
+ }
109
+
110
+ self . invoice_request . amount_msats = Some ( amount_msats) ;
111
+ Ok ( self )
112
+ }
113
+
114
+ ///
115
+ pub fn features ( mut self , features : OfferFeatures ) -> Self {
116
+ self . invoice_request . features = Some ( features) ;
117
+ self
118
+ }
119
+
120
+ ///
121
+ pub fn quantity ( mut self , quantity : u64 ) -> Result < Self , SemanticError > {
122
+ if !self . offer . is_valid_quantity ( quantity) {
123
+ return Err ( SemanticError :: InvalidQuantity ) ;
124
+ }
125
+
126
+ self . invoice_request . quantity = Some ( quantity) ;
127
+ Ok ( self )
128
+ }
129
+
130
+ ///
131
+ pub fn payer_note ( mut self , payer_note : String ) -> Self {
132
+ self . invoice_request . payer_note = Some ( payer_note) ;
133
+ self
134
+ }
135
+
136
+ /// Builds the [`InvoiceRequest`] after checking for valid semantics.
137
+ pub fn build ( self ) -> Result < UnsignedInvoiceRequest < ' a > , SemanticError > {
138
+ let chain = self . invoice_request . chain . unwrap_or_else ( || self . offer . implied_chain ( ) ) ;
139
+ if !self . offer . supports_chain ( chain) {
140
+ return Err ( SemanticError :: UnsupportedChain ) ;
141
+ }
142
+
143
+ if self . offer . amount ( ) . is_some ( ) && self . invoice_request . amount_msats . is_none ( ) {
144
+ return Err ( SemanticError :: MissingAmount ) ;
145
+ }
146
+
147
+ if self . offer . expects_quantity ( ) && self . invoice_request . quantity . is_none ( ) {
148
+ return Err ( SemanticError :: InvalidQuantity ) ;
149
+ }
150
+
151
+ let InvoiceRequestBuilder { offer, invoice_request } = self ;
152
+ Ok ( UnsignedInvoiceRequest { offer, invoice_request } )
153
+ }
154
+ }
155
+
156
+ /// A semantically valid [`InvoiceRequest`] that hasn't been signed.
157
+ pub struct UnsignedInvoiceRequest < ' a > {
158
+ offer : & ' a Offer ,
159
+ invoice_request : InvoiceRequestContents ,
160
+ }
161
+
162
+ impl < ' a > UnsignedInvoiceRequest < ' a > {
163
+ /// Signs the invoice request using the given function.
164
+ pub fn sign < F > ( self , sign : F ) -> Result < InvoiceRequest , secp256k1:: Error >
165
+ where F : FnOnce ( & Message ) -> Signature
166
+ {
167
+ // Use the offer bytes instead of the offer TLV stream as the offer may have contained
168
+ // unknown TLV records, which are not stored in `OfferContents`.
169
+ let ( payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) =
170
+ self . invoice_request . as_tlv_stream ( ) ;
171
+ let offer_bytes = WithoutLength ( & self . offer . bytes ) ;
172
+ let unsigned_tlv_stream = ( payer_tlv_stream, offer_bytes, invoice_request_tlv_stream) ;
173
+
174
+ let mut bytes = Vec :: new ( ) ;
175
+ unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
176
+
177
+ let pubkey = self . invoice_request . payer_id ;
178
+ let signature = Some ( merkle:: sign_message ( sign, SIGNATURE_TAG , & bytes, pubkey) ?) ;
179
+
180
+ // Append the signature TLV record to the bytes.
181
+ let signature_tlv_stream = merkle:: reference:: SignatureTlvStream {
182
+ signature : signature. as_ref ( ) ,
183
+ } ;
184
+ signature_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
185
+
186
+ Ok ( InvoiceRequest {
187
+ bytes,
188
+ contents : self . invoice_request ,
189
+ signature,
190
+ } )
191
+ }
192
+ }
193
+
25
194
///
26
195
pub struct InvoiceRequest {
27
196
bytes : Vec < u8 > ,
@@ -182,8 +351,7 @@ impl TryFrom<ParsedInvoiceRequest> for InvoiceRequest {
182
351
) ?;
183
352
184
353
if let Some ( signature) = & signature {
185
- let tag = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
186
- merkle:: verify_signature ( signature, tag, & bytes, contents. payer_id ) ?;
354
+ merkle:: verify_signature ( signature, SIGNATURE_TAG , & bytes, contents. payer_id ) ?;
187
355
}
188
356
189
357
Ok ( InvoiceRequest { bytes, contents, signature } )
@@ -203,25 +371,22 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
203
371
let payer = PayerContents ( payer_info. map ( Into :: into) ) ;
204
372
let offer = OfferContents :: try_from ( offer_tlv_stream) ?;
205
373
206
- let chain = match chain {
207
- None => None ,
208
- Some ( chain) if chain == offer. chain ( ) => Some ( chain) ,
209
- Some ( _) => return Err ( SemanticError :: UnsupportedChain ) ,
210
- } ;
374
+ if !offer. supports_chain ( chain. unwrap_or_else ( || offer. implied_chain ( ) ) ) {
375
+ return Err ( SemanticError :: UnsupportedChain ) ;
376
+ }
211
377
212
378
// TODO: Determine whether quantity should be accounted for
213
379
let amount_msats = match ( offer. amount ( ) , amount. map ( Into :: into) ) {
214
380
// TODO: Handle currency case
215
- ( Some ( Amount :: Currency { .. } ) , _) => return Err ( SemanticError :: UnexpectedCurrency ) ,
216
381
( Some ( _) , None ) => return Err ( SemanticError :: MissingAmount ) ,
217
- ( Some ( Amount :: Bitcoin { amount_msats : offer_amount_msats } ) , Some ( amount_msats) ) => {
218
- if amount_msats < * offer_amount_msats {
382
+ ( Some ( _ ) , Some ( amount_msats) ) => {
383
+ if !offer . is_sufficient_amount ( amount_msats) {
219
384
return Err ( SemanticError :: InsufficientAmount ) ;
220
385
} else {
221
386
Some ( amount_msats)
222
387
}
223
388
} ,
224
- ( _ , amount_msats) => amount_msats,
389
+ ( None , amount_msats) => amount_msats,
225
390
} ;
226
391
227
392
if let Some ( features) = & features {
0 commit comments