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
- use util:: ser:: { Writeable , Writer } ;
25
+ use util:: ser:: { WithoutLength , Writeable , Writer } ;
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
+ }
24
127
25
128
///
26
129
pub struct InvoiceRequest {
@@ -111,8 +214,7 @@ impl FromStr for InvoiceRequest {
111
214
let contents = InvoiceRequestContents :: try_from ( tlv_stream) ?;
112
215
113
216
if let Some ( signature) = & contents. signature {
114
- let tag = concat ! ( "lightning" , "invoice_request" , "signature" ) ;
115
- merkle:: verify_signature ( signature, tag, & bytes, contents. payer_id ) ?;
217
+ merkle:: verify_signature ( signature, SIGNATURE_TAG , & bytes, contents. payer_id ) ?;
116
218
}
117
219
118
220
Ok ( InvoiceRequest { bytes, contents } )
@@ -133,25 +235,22 @@ impl TryFrom<FullInvoiceRequestTlvStream> for InvoiceRequestContents {
133
235
let payer = PayerContents ( payer_info. map ( Into :: into) ) ;
134
236
let offer = OfferContents :: try_from ( offer_tlv_stream) ?;
135
237
136
- let chain = match chain {
137
- None => None ,
138
- Some ( chain) if chain == offer. chain ( ) => Some ( chain) ,
139
- Some ( _) => return Err ( SemanticError :: UnsupportedChain ) ,
140
- } ;
238
+ if !offer. supports_chain ( chain. unwrap_or_else ( || offer. implied_chain ( ) ) ) {
239
+ return Err ( SemanticError :: UnsupportedChain ) ;
240
+ }
141
241
142
242
// TODO: Determine whether quantity should be accounted for
143
243
let amount_msats = match ( offer. amount ( ) , amount. map ( Into :: into) ) {
144
244
// TODO: Handle currency case
145
- ( Some ( Amount :: Currency { .. } ) , _) => return Err ( SemanticError :: UnexpectedCurrency ) ,
146
245
( Some ( _) , None ) => return Err ( SemanticError :: MissingAmount ) ,
147
- ( Some ( Amount :: Bitcoin { amount_msats : offer_amount_msats } ) , Some ( amount_msats) ) => {
148
- if amount_msats < * offer_amount_msats {
246
+ ( Some ( _ ) , Some ( amount_msats) ) => {
247
+ if !offer . is_sufficient_amount ( amount_msats) {
149
248
return Err ( SemanticError :: InsufficientAmount ) ;
150
249
} else {
151
250
Some ( amount_msats)
152
251
}
153
252
} ,
154
- ( _ , amount_msats) => amount_msats,
253
+ ( None , amount_msats) => amount_msats,
155
254
} ;
156
255
157
256
if let Some ( features) = & features {
0 commit comments