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
25
63
use prelude:: * ;
26
64
65
+ ///
66
+ const SIGNATURE_TAG : & ' static str = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
67
+
68
+ /// Builds an [`InvoiceRequest`] from an [`Offer`] for the user-pays-merchant flow.
69
+ ///
70
+ /// See [module-level documentation] for usage.
71
+ ///
72
+ /// [module-level documentation]: self
73
+ pub struct InvoiceRequestBuilder < ' a > {
74
+ offer : & ' a Offer ,
75
+ invoice_request : InvoiceRequestContents ,
76
+ }
77
+
78
+ impl < ' a > InvoiceRequestBuilder < ' a > {
79
+ pub ( super ) fn new ( offer : & ' a Offer , payer_id : PublicKey ) -> Self {
80
+ Self {
81
+ offer,
82
+ invoice_request : InvoiceRequestContents {
83
+ payer : PayerContents ( None ) , offer : offer. contents . clone ( ) , chain : None ,
84
+ amount_msats : None , features : None , quantity : None , payer_id, payer_note : None ,
85
+ } ,
86
+ }
87
+ }
88
+
89
+ ///
90
+ pub fn payer_info ( mut self , payer_info : Vec < u8 > ) -> Self {
91
+ self . invoice_request . payer = PayerContents ( Some ( payer_info) ) ;
92
+ self
93
+ }
94
+
95
+ ///
96
+ pub fn chain ( mut self , network : Network ) -> Result < Self , SemanticError > {
97
+ let chain_hash = ChainHash :: using_genesis_block ( network) ;
98
+ if !self . offer . supports_chain ( chain_hash) {
99
+ return Err ( SemanticError :: UnsupportedChain )
100
+ }
101
+
102
+ self . invoice_request . chain = Some ( chain_hash) ;
103
+ Ok ( self )
104
+ }
105
+
106
+ ///
107
+ pub fn amount_msats ( mut self , amount_msats : u64 ) -> Result < Self , SemanticError > {
108
+ if !self . offer . is_sufficient_amount ( amount_msats) {
109
+ return Err ( SemanticError :: InsufficientAmount ) ;
110
+ }
111
+
112
+ self . invoice_request . amount_msats = Some ( amount_msats) ;
113
+ Ok ( self )
114
+ }
115
+
116
+ ///
117
+ pub fn features ( mut self , features : OfferFeatures ) -> Self {
118
+ self . invoice_request . features = Some ( features) ;
119
+ self
120
+ }
121
+
122
+ ///
123
+ pub fn quantity ( mut self , quantity : u64 ) -> Result < Self , SemanticError > {
124
+ if !self . offer . is_valid_quantity ( quantity) {
125
+ return Err ( SemanticError :: InvalidQuantity ) ;
126
+ }
127
+
128
+ self . invoice_request . quantity = Some ( quantity) ;
129
+ Ok ( self )
130
+ }
131
+
132
+ ///
133
+ pub fn payer_note ( mut self , payer_note : String ) -> Self {
134
+ self . invoice_request . payer_note = Some ( payer_note) ;
135
+ self
136
+ }
137
+
138
+ /// Builds the [`InvoiceRequest`] after checking for valid semantics.
139
+ pub fn build ( self ) -> Result < UnsignedInvoiceRequest < ' a > , SemanticError > {
140
+ let chain = self . invoice_request . chain . unwrap_or_else ( || self . offer . implied_chain ( ) ) ;
141
+ if !self . offer . supports_chain ( chain) {
142
+ return Err ( SemanticError :: UnsupportedChain ) ;
143
+ }
144
+
145
+ if self . offer . amount ( ) . is_some ( ) && self . invoice_request . amount_msats . is_none ( ) {
146
+ return Err ( SemanticError :: MissingAmount ) ;
147
+ }
148
+
149
+ if self . offer . expects_quantity ( ) && self . invoice_request . quantity . is_none ( ) {
150
+ return Err ( SemanticError :: InvalidQuantity ) ;
151
+ }
152
+
153
+ let InvoiceRequestBuilder { offer, invoice_request } = self ;
154
+ Ok ( UnsignedInvoiceRequest { offer, invoice_request } )
155
+ }
156
+ }
157
+
158
+ /// A semantically valid [`InvoiceRequest`] that hasn't been signed.
159
+ pub struct UnsignedInvoiceRequest < ' a > {
160
+ offer : & ' a Offer ,
161
+ invoice_request : InvoiceRequestContents ,
162
+ }
163
+
164
+ impl < ' a > UnsignedInvoiceRequest < ' a > {
165
+ /// Signs the invoice request using the given function.
166
+ pub fn sign < F > ( self , sign : F ) -> Result < InvoiceRequest , secp256k1:: Error >
167
+ where F : FnOnce ( & Message ) -> Signature
168
+ {
169
+ // Use the offer bytes instead of the offer TLV stream as the offer may have contained
170
+ // unknown TLV records, which are not stored in `OfferContents`.
171
+ let ( payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) =
172
+ self . invoice_request . as_tlv_stream ( ) ;
173
+ let offer_bytes = WithoutLength ( & self . offer . bytes ) ;
174
+ let unsigned_tlv_stream = ( payer_tlv_stream, offer_bytes, invoice_request_tlv_stream) ;
175
+
176
+ let mut bytes = Vec :: new ( ) ;
177
+ unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
178
+
179
+ let pubkey = self . invoice_request . payer_id ;
180
+ let signature = Some ( merkle:: sign_message ( sign, SIGNATURE_TAG , & bytes, pubkey) ?) ;
181
+
182
+ // Append the signature TLV record to the bytes.
183
+ let signature_tlv_stream = merkle:: reference:: SignatureTlvStream {
184
+ signature : signature. as_ref ( ) ,
185
+ } ;
186
+ signature_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
187
+
188
+ Ok ( InvoiceRequest {
189
+ bytes,
190
+ contents : self . invoice_request ,
191
+ signature,
192
+ } )
193
+ }
194
+ }
195
+
27
196
///
28
197
pub struct InvoiceRequest {
29
198
bytes : Vec < u8 > ,
@@ -184,8 +353,7 @@ impl TryFrom<ParsedInvoiceRequest> for InvoiceRequest {
184
353
) ?;
185
354
186
355
if let Some ( signature) = & signature {
187
- let tag = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
188
- merkle:: verify_signature ( signature, tag, & bytes, contents. payer_id ) ?;
356
+ merkle:: verify_signature ( signature, SIGNATURE_TAG , & bytes, contents. payer_id ) ?;
189
357
}
190
358
191
359
Ok ( InvoiceRequest { bytes, contents, signature } )
@@ -205,25 +373,22 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
205
373
let payer = PayerContents ( payer_info. map ( Into :: into) ) ;
206
374
let offer = OfferContents :: try_from ( offer_tlv_stream) ?;
207
375
208
- let chain = match chain {
209
- None => None ,
210
- Some ( chain) if chain == offer. chain ( ) => Some ( chain) ,
211
- Some ( _) => return Err ( SemanticError :: UnsupportedChain ) ,
212
- } ;
376
+ if !offer. supports_chain ( chain. unwrap_or_else ( || offer. implied_chain ( ) ) ) {
377
+ return Err ( SemanticError :: UnsupportedChain ) ;
378
+ }
213
379
214
380
// TODO: Determine whether quantity should be accounted for
215
381
let amount_msats = match ( offer. amount ( ) , amount. map ( Into :: into) ) {
216
382
// TODO: Handle currency case
217
- ( Some ( Amount :: Currency { .. } ) , _) => return Err ( SemanticError :: UnexpectedCurrency ) ,
218
383
( Some ( _) , None ) => return Err ( SemanticError :: MissingAmount ) ,
219
- ( Some ( Amount :: Bitcoin { amount_msats : offer_amount_msats } ) , Some ( amount_msats) ) => {
220
- if amount_msats < * offer_amount_msats {
384
+ ( Some ( _ ) , Some ( amount_msats) ) => {
385
+ if !offer . is_sufficient_amount ( amount_msats) {
221
386
return Err ( SemanticError :: InsufficientAmount ) ;
222
387
} else {
223
388
Some ( amount_msats)
224
389
}
225
390
} ,
226
- ( _ , amount_msats) => amount_msats,
391
+ ( None , amount_msats) => amount_msats,
227
392
} ;
228
393
229
394
if let Some ( features) = & features {
0 commit comments