@@ -14,8 +14,9 @@ use bitcoin::bech32;
14
14
use bitcoin:: bech32:: FromBase32 ;
15
15
use bitcoin:: blockdata:: constants:: genesis_block;
16
16
use bitcoin:: hash_types:: BlockHash ;
17
+ use bitcoin:: hashes:: { Hash , sha256} ;
17
18
use bitcoin:: network:: constants:: Network ;
18
- use bitcoin:: secp256k1:: { PublicKey , XOnlyPublicKey } ;
19
+ use bitcoin:: secp256k1:: { Message , PublicKey , Secp256k1 , XOnlyPublicKey , self } ;
19
20
use bitcoin:: secp256k1:: schnorr:: Signature ;
20
21
use core:: convert:: TryFrom ;
21
22
use core:: str:: FromStr ;
@@ -30,6 +31,8 @@ use prelude::*;
30
31
#[ cfg( feature = "std" ) ]
31
32
use std:: time:: SystemTime ;
32
33
34
+ mod merkle;
35
+
33
36
///
34
37
#[ derive( Clone , Debug ) ]
35
38
pub struct Offer {
@@ -184,6 +187,15 @@ pub enum Destination {
184
187
Paths ( Vec < BlindedPath > ) ,
185
188
}
186
189
190
+ impl Destination {
191
+ fn node_id ( & self ) -> XOnlyPublicKey {
192
+ match self {
193
+ Destination :: NodeId ( node_id) => * node_id,
194
+ Destination :: Paths ( paths) => unimplemented ! ( ) ,
195
+ }
196
+ }
197
+ }
198
+
187
199
///
188
200
#[ derive( Clone , Debug ) ]
189
201
pub struct SendInvoice {
@@ -255,6 +267,10 @@ struct Recurrence {
255
267
256
268
impl_writeable ! ( Recurrence , { time_unit, period } ) ;
257
269
270
+ /// An offer parsed from a bech32-encoded string as a TLV stream and the corresponding bytes. The
271
+ /// latter is used for signature verification.
272
+ struct ParsedOffer ( OfferTlvStream , Vec < u8 > ) ;
273
+
258
274
/// Error when parsing a bech32 encoded message using [`str::parse`].
259
275
#[ derive( Debug , PartialEq ) ]
260
276
pub enum ParseError {
@@ -293,6 +309,8 @@ pub enum SemanticError {
293
309
InvalidQuantity ,
294
310
///
295
311
UnexpectedRefund ,
312
+ ///
313
+ InvalidSignature ( secp256k1:: Error ) ,
296
314
}
297
315
298
316
impl From < bech32:: Error > for ParseError {
@@ -313,23 +331,28 @@ impl From<SemanticError> for ParseError {
313
331
}
314
332
}
315
333
334
+ impl From < secp256k1:: Error > for SemanticError {
335
+ fn from ( error : secp256k1:: Error ) -> Self {
336
+ Self :: InvalidSignature ( error)
337
+ }
338
+ }
339
+
316
340
impl FromStr for Offer {
317
341
type Err = ParseError ;
318
342
319
343
fn from_str ( s : & str ) -> Result < Self , <Self as FromStr >:: Err > {
320
- let tlv_stream = OfferTlvStream :: from_str ( s) ?;
321
- Ok ( Offer :: try_from ( tlv_stream) ?)
344
+ Ok ( Offer :: try_from ( ParsedOffer :: from_str ( s) ?) ?)
322
345
}
323
346
}
324
347
325
- impl TryFrom < OfferTlvStream > for Offer {
348
+ impl TryFrom < ParsedOffer > for Offer {
326
349
type Error = SemanticError ;
327
350
328
- fn try_from ( tlv_stream : OfferTlvStream ) -> Result < Self , Self :: Error > {
329
- let OfferTlvStream {
351
+ fn try_from ( offer : ParsedOffer ) -> Result < Self , Self :: Error > {
352
+ let ParsedOffer ( OfferTlvStream {
330
353
chains, currency, amount, description, features, absolute_expiry, paths, issuer,
331
354
quantity_min, quantity_max, recurrence, node_id, send_invoice, refund_for, signature,
332
- } = tlv_stream ;
355
+ } , data ) = offer ;
333
356
334
357
let supported_chains = [
335
358
genesis_block ( Network :: Bitcoin ) . block_hash ( ) ,
@@ -394,6 +417,14 @@ impl TryFrom<OfferTlvStream> for Offer {
394
417
( Some ( _) , _) => Some ( SendInvoice { refund_for } ) ,
395
418
} ;
396
419
420
+
421
+ if let Some ( signature) = & signature {
422
+ let tag = sha256:: Hash :: hash ( concat ! ( "lightning" , "offer" , "signature" ) . as_bytes ( ) ) ;
423
+ let message = Message :: from_slice ( & merkle:: tagged_root_hash ( tag, & data) ) . unwrap ( ) ;
424
+ let secp_ctx = Secp256k1 :: verification_only ( ) ;
425
+ secp_ctx. verify_schnorr ( signature, & message, & destination. node_id ( ) ) ?;
426
+ }
427
+
397
428
Ok ( Offer {
398
429
chains,
399
430
amount,
@@ -414,7 +445,7 @@ impl TryFrom<OfferTlvStream> for Offer {
414
445
415
446
const OFFER_BECH32_HRP : & str = "lno" ;
416
447
417
- impl FromStr for OfferTlvStream {
448
+ impl FromStr for ParsedOffer {
418
449
type Err = ParseError ;
419
450
420
451
fn from_str ( s : & str ) -> Result < Self , <Self as FromStr >:: Err > {
@@ -434,7 +465,7 @@ impl FromStr for OfferTlvStream {
434
465
}
435
466
436
467
let data = Vec :: < u8 > :: from_base32 ( & data) ?;
437
- Ok ( Readable :: read ( & mut & data[ ..] ) ?)
468
+ Ok ( ParsedOffer ( Readable :: read ( & mut & data[ ..] ) ?, data ) )
438
469
}
439
470
}
440
471
@@ -460,7 +491,7 @@ impl core::fmt::Display for OfferTlvStream {
460
491
461
492
#[ cfg( test) ]
462
493
mod tests {
463
- use super :: { Offer , OfferTlvStream , ParseError } ;
494
+ use super :: { Offer , ParseError , ParsedOffer } ;
464
495
use bitcoin:: bech32;
465
496
use ln:: msgs:: DecodeError ;
466
497
@@ -487,7 +518,7 @@ mod tests {
487
518
] ;
488
519
for encoded_offer in & offers {
489
520
// TODO: Use Offer once Destination semantics are finalized.
490
- if let Err ( e) = encoded_offer. parse :: < OfferTlvStream > ( ) {
521
+ if let Err ( e) = encoded_offer. parse :: < ParsedOffer > ( ) {
491
522
panic ! ( "Invalid offer ({:?}): {}" , e, encoded_offer) ;
492
523
}
493
524
}
0 commit comments