9
9
10
10
//! Data structures and encoding for `invoice_request` messages.
11
11
12
+ use bitcoin:: blockdata:: constants:: genesis_block;
12
13
use bitcoin:: hash_types:: BlockHash ;
13
- use bitcoin:: secp256k1:: PublicKey ;
14
+ use bitcoin:: network:: constants:: Network ;
15
+ use bitcoin:: secp256k1:: { Message , PublicKey , self } ;
14
16
use bitcoin:: secp256k1:: schnorr:: Signature ;
15
17
use core:: convert:: TryFrom ;
16
18
use core:: str:: FromStr ;
17
19
use io;
18
20
use ln:: features:: OfferFeatures ;
19
21
use offers:: merkle:: { SignatureTlvStream , self } ;
20
- use offers:: offer:: { Amount , OfferContents , OfferTlvStream , self } ;
22
+ use offers:: offer:: { Offer , OfferContents , OfferTlvStream , self } ;
21
23
use offers:: parse:: { Bech32Encode , ParseError , SemanticError } ;
22
24
use offers:: payer:: { PayerContents , PayerTlvStream , self } ;
23
25
use util:: ser:: { Readable , WithoutLength , Writeable , Writer } ;
24
26
27
+ ///
28
+ const SIGNATURE_TAG : & ' static str = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
29
+
30
+ ///
31
+ pub struct InvoiceRequestBuilder < ' a > {
32
+ offer : & ' a Offer ,
33
+ invoice_request : InvoiceRequestContents ,
34
+ }
35
+
36
+ impl < ' a > InvoiceRequestBuilder < ' a > {
37
+ pub ( super ) fn new ( offer : & ' a Offer , payer_id : PublicKey ) -> Self {
38
+ Self {
39
+ offer,
40
+ invoice_request : InvoiceRequestContents {
41
+ payer : PayerContents ( None ) , offer : offer. contents . clone ( ) , chain : None ,
42
+ amount_msats : None , features : None , quantity : None , payer_id, payer_note : None ,
43
+ signature : None ,
44
+ } ,
45
+ }
46
+ }
47
+
48
+ ///
49
+ pub fn payer_info ( mut self , payer_info : Vec < u8 > ) -> Self {
50
+ self . invoice_request . payer = PayerContents ( Some ( payer_info) ) ;
51
+ self
52
+ }
53
+
54
+ ///
55
+ pub fn chain ( mut self , network : Network ) -> Result < Self , SemanticError > {
56
+ let block_hash = genesis_block ( network) . block_hash ( ) ;
57
+ if !self . offer . supports_chain ( block_hash) {
58
+ return Err ( SemanticError :: UnsupportedChain )
59
+ }
60
+
61
+ self . invoice_request . chain = Some ( block_hash) ;
62
+ Ok ( self )
63
+ }
64
+
65
+ ///
66
+ pub fn amount_msats ( mut self , amount_msats : u64 ) -> Result < Self , SemanticError > {
67
+ if !self . offer . is_sufficient_amount ( amount_msats) {
68
+ return Err ( SemanticError :: InsufficientAmount ) ;
69
+ }
70
+
71
+ self . invoice_request . amount_msats = Some ( amount_msats) ;
72
+ Ok ( self )
73
+ }
74
+
75
+ ///
76
+ pub fn features ( mut self , features : OfferFeatures ) -> Self {
77
+ self . invoice_request . features = Some ( features) ;
78
+ self
79
+ }
80
+
81
+ ///
82
+ pub fn quantity ( mut self , quantity : u64 ) -> Result < Self , SemanticError > {
83
+ if !self . offer . is_valid_quantity ( quantity) {
84
+ return Err ( SemanticError :: InvalidQuantity ) ;
85
+ }
86
+
87
+ self . invoice_request . quantity = Some ( quantity) ;
88
+ Ok ( self )
89
+ }
90
+
91
+ ///
92
+ pub fn payer_note ( mut self , payer_note : String ) -> Self {
93
+ self . invoice_request . payer_note = Some ( payer_note) ;
94
+ self
95
+ }
96
+
97
+ ///
98
+ pub fn build_signed < F > ( mut self , sign : F ) -> Result < InvoiceRequest , secp256k1:: Error >
99
+ where F : FnOnce ( & Message ) -> Signature
100
+ {
101
+ // Use the offer bytes instead of the offer TLV stream as the offer may have contained
102
+ // unknown TLV records, which are not stored in `OfferContents`.
103
+ let ( payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream, _) =
104
+ self . invoice_request . as_tlv_stream ( ) ;
105
+ let offer_bytes = WithoutLength ( & self . offer . bytes ) ;
106
+ let unsigned_tlv_stream = ( payer_tlv_stream, offer_bytes, invoice_request_tlv_stream) ;
107
+
108
+ let mut bytes = Vec :: new ( ) ;
109
+ unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
110
+
111
+ let pubkey = self . invoice_request . payer_id ;
112
+ let signature = merkle:: sign_message ( sign, SIGNATURE_TAG , & bytes, pubkey) ?;
113
+ self . invoice_request . signature = Some ( signature) ;
114
+
115
+ // Append the signature TLV record to the bytes.
116
+ let signature_tlv_stream = merkle:: reference:: SignatureTlvStream {
117
+ signature : self . invoice_request . signature . as_ref ( ) ,
118
+ } ;
119
+ signature_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
120
+
121
+ Ok ( InvoiceRequest {
122
+ bytes,
123
+ contents : self . invoice_request ,
124
+ } )
125
+ }
126
+ }
127
+
25
128
///
26
129
pub struct InvoiceRequest {
27
130
bytes : Vec < u8 > ,
@@ -169,8 +272,7 @@ impl FromStr for InvoiceRequest {
169
272
let contents = InvoiceRequestContents :: try_from ( tlv_stream) ?;
170
273
171
274
if let Some ( signature) = & contents. signature {
172
- let tag = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
173
- merkle:: verify_signature ( signature, tag, & bytes, contents. payer_id ) ?;
275
+ merkle:: verify_signature ( signature, SIGNATURE_TAG , & bytes, contents. payer_id ) ?;
174
276
}
175
277
176
278
Ok ( InvoiceRequest { bytes, contents } )
@@ -191,25 +293,22 @@ impl TryFrom<FullInvoiceRequestTlvStream> for InvoiceRequestContents {
191
293
let payer = PayerContents ( payer_info. map ( Into :: into) ) ;
192
294
let offer = OfferContents :: try_from ( offer_tlv_stream) ?;
193
295
194
- let chain = match chain {
195
- None => None ,
196
- Some ( chain) if chain == offer. chain ( ) => Some ( chain) ,
197
- Some ( _) => return Err ( SemanticError :: UnsupportedChain ) ,
198
- } ;
296
+ if !offer. supports_chain ( chain. unwrap_or_else ( || offer. implied_chain ( ) ) ) {
297
+ return Err ( SemanticError :: UnsupportedChain ) ;
298
+ }
199
299
200
300
// TODO: Determine whether quantity should be accounted for
201
301
let amount_msats = match ( offer. amount ( ) , amount. map ( Into :: into) ) {
202
302
// TODO: Handle currency case
203
- ( Some ( Amount :: Currency { .. } ) , _) => return Err ( SemanticError :: UnexpectedCurrency ) ,
204
303
( Some ( _) , None ) => return Err ( SemanticError :: MissingAmount ) ,
205
- ( Some ( Amount :: Bitcoin { amount_msats : offer_amount_msats } ) , Some ( amount_msats) ) => {
206
- if amount_msats < * offer_amount_msats {
304
+ ( Some ( _ ) , Some ( amount_msats) ) => {
305
+ if !offer . is_sufficient_amount ( amount_msats) {
207
306
return Err ( SemanticError :: InsufficientAmount ) ;
208
307
} else {
209
308
Some ( amount_msats)
210
309
}
211
310
} ,
212
- ( _ , amount_msats) => amount_msats,
311
+ ( None , amount_msats) => amount_msats,
213
312
} ;
214
313
215
314
if let Some ( features) = & features {
0 commit comments