10
10
//! Data structures and encoding for `invoice_request` messages.
11
11
12
12
use bitcoin:: hash_types:: BlockHash ;
13
- use bitcoin:: secp256k1:: PublicKey ;
13
+ use bitcoin:: hashes:: { Hash , sha256} ;
14
+ use bitcoin:: secp256k1:: { Message , PublicKey , Secp256k1 , self } ;
14
15
use bitcoin:: secp256k1:: schnorr:: Signature ;
15
16
use core:: convert:: TryFrom ;
16
17
use core:: str:: FromStr ;
17
18
use ln:: features:: OfferFeatures ;
18
19
use offers:: PayerTlvStream ;
19
- use offers:: merkle:: SignatureTlvStream ;
20
- use offers:: offer:: { OfferContents , OfferTlvStream } ;
20
+ use offers:: merkle:: { SignatureTlvStream , self } ;
21
+ use offers:: offer:: { Amount , OfferContents , OfferTlvStream } ;
21
22
use offers:: parse:: { Bech32Encode , ParseError , SemanticError } ;
22
23
23
24
///
@@ -30,15 +31,33 @@ pub struct InvoiceRequest {
30
31
pub ( crate ) struct InvoiceRequestContents {
31
32
offer : OfferContents ,
32
33
chain : Option < BlockHash > ,
33
- amount_msat : Option < u64 > ,
34
+ amount_msats : Option < u64 > ,
34
35
features : Option < OfferFeatures > ,
35
36
quantity : Option < u64 > ,
36
- payer_id : Option < PublicKey > ,
37
+ payer_id : PublicKey ,
37
38
payer_note : Option < String > ,
38
39
payer_info : Option < Vec < u8 > > ,
39
40
signature : Option < Signature > ,
40
41
}
41
42
43
+ impl InvoiceRequestContents {
44
+ pub fn verify_signature ( & self , bytes : & [ u8 ] ) -> Result < ( ) , secp256k1:: Error > {
45
+ if let Some ( signature) = & self . signature {
46
+ let tag = sha256:: Hash :: hash (
47
+ concat ! ( "lightning" , "invoice_request" , "signature" ) . as_bytes ( )
48
+ ) ;
49
+ let merkle_root = merkle:: root_hash ( bytes) ;
50
+ let digest = Message :: from_slice ( & merkle:: tagged_hash ( tag, merkle_root) ) . unwrap ( ) ;
51
+ let pubkey = self . payer_id . into ( ) ;
52
+ let secp_ctx = Secp256k1 :: verification_only ( ) ;
53
+ secp_ctx. verify_schnorr ( signature, & digest, & pubkey)
54
+ } else {
55
+ Ok ( ( ) )
56
+ }
57
+ }
58
+
59
+ }
60
+
42
61
impl AsRef < [ u8 ] > for InvoiceRequest {
43
62
fn as_ref ( & self ) -> & [ u8 ] {
44
63
& self . bytes
@@ -69,6 +88,8 @@ impl FromStr for InvoiceRequest {
69
88
fn from_str ( s : & str ) -> Result < Self , <Self as FromStr >:: Err > {
70
89
let ( tlv_stream, bytes) = InvoiceRequest :: from_bech32_str ( s) ?;
71
90
let contents = InvoiceRequestContents :: try_from ( tlv_stream) ?;
91
+ contents. verify_signature ( & bytes) ?;
92
+
72
93
Ok ( InvoiceRequest { bytes, contents } )
73
94
}
74
95
}
@@ -93,16 +114,44 @@ impl TryFrom<FullInvoiceRequestTlvStream> for InvoiceRequestContents {
93
114
} ;
94
115
95
116
// TODO: Check remaining fields against the reflected offer
96
- let amount_msat = amount. map ( Into :: into) ;
97
117
98
- let quantity = quantity. map ( Into :: into) ;
118
+ // TODO: Determine whether quantity should be accounted for
119
+ let amount_msats = match ( offer. amount ( ) , amount. map ( Into :: into) ) {
120
+ // TODO: Handle currency case
121
+ ( Some ( Amount :: Currency { .. } ) , _) => return Err ( SemanticError :: UnexpectedCurrency ) ,
122
+ ( Some ( _) , None ) => return Err ( SemanticError :: MissingAmount ) ,
123
+ ( Some ( Amount :: Bitcoin { amount_msats : offer_amount_msats } ) , Some ( amount_msats) ) => {
124
+ if amount_msats < * offer_amount_msats {
125
+ return Err ( SemanticError :: InsufficientAmount ) ;
126
+ } else {
127
+ Some ( amount_msats)
128
+ }
129
+ } ,
130
+ ( _, amount_msats) => amount_msats,
131
+ } ;
132
+
133
+ if let Some ( features) = & features {
134
+ if features. requires_unknown_bits ( ) {
135
+ return Err ( SemanticError :: UnknownRequiredFeatures ) ;
136
+ }
137
+ }
138
+
139
+ let quantity = match quantity. map ( Into :: into) {
140
+ Some ( quantity) if offer. is_valid_quantity ( quantity) => Some ( quantity) ,
141
+ _ => return Err ( SemanticError :: InvalidQuantity ) ,
142
+ } ;
143
+
144
+ let payer_id = match payer_id {
145
+ None => return Err ( SemanticError :: MissingPayerId ) ,
146
+ Some ( payer_id) => payer_id,
147
+ } ;
99
148
100
149
let payer_note = payer_note. map ( Into :: into) ;
101
150
102
151
let payer_info = payer_info. map ( Into :: into) ;
103
152
104
153
Ok ( InvoiceRequestContents {
105
- offer, chain, amount_msat , features, quantity, payer_id, payer_note, payer_info,
154
+ offer, chain, amount_msats , features, quantity, payer_id, payer_note, payer_info,
106
155
signature,
107
156
} )
108
157
}
0 commit comments