@@ -14,8 +14,9 @@ use bitcoin::bech32;
1414use bitcoin:: bech32:: FromBase32 ;
1515use bitcoin:: blockdata:: constants:: genesis_block;
1616use bitcoin:: hash_types:: BlockHash ;
17+ use bitcoin:: hashes:: { Hash , sha256} ;
1718use bitcoin:: network:: constants:: Network ;
18- use bitcoin:: secp256k1:: { PublicKey , XOnlyPublicKey } ;
19+ use bitcoin:: secp256k1:: { Message , PublicKey , Secp256k1 , XOnlyPublicKey , self } ;
1920use bitcoin:: secp256k1:: schnorr:: Signature ;
2021use core:: convert:: TryFrom ;
2122use core:: str:: FromStr ;
@@ -30,6 +31,8 @@ use prelude::*;
3031#[ cfg( feature = "std" ) ]
3132use std:: time:: SystemTime ;
3233
34+ mod merkle;
35+
3336///
3437#[ derive( Clone , Debug ) ]
3538pub struct Offer {
@@ -184,6 +187,15 @@ pub enum Destination {
184187 Paths ( Vec < BlindedPath > ) ,
185188}
186189
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+
187199///
188200#[ derive( Clone , Debug ) ]
189201pub struct SendInvoice {
@@ -255,6 +267,10 @@ struct Recurrence {
255267
256268impl_writeable ! ( Recurrence , { time_unit, period } ) ;
257269
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+
258274/// Error when parsing a bech32 encoded message using [`str::parse`].
259275#[ derive( Debug , PartialEq ) ]
260276pub enum ParseError {
@@ -293,6 +309,8 @@ pub enum SemanticError {
293309 InvalidQuantity ,
294310 ///
295311 UnexpectedRefund ,
312+ ///
313+ InvalidSignature ( secp256k1:: Error ) ,
296314}
297315
298316impl From < bech32:: Error > for ParseError {
@@ -313,23 +331,28 @@ impl From<SemanticError> for ParseError {
313331 }
314332}
315333
334+ impl From < secp256k1:: Error > for SemanticError {
335+ fn from ( error : secp256k1:: Error ) -> Self {
336+ Self :: InvalidSignature ( error)
337+ }
338+ }
339+
316340impl FromStr for Offer {
317341 type Err = ParseError ;
318342
319343 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) ?) ?)
322345 }
323346}
324347
325- impl TryFrom < OfferTlvStream > for Offer {
348+ impl TryFrom < ParsedOffer > for Offer {
326349 type Error = SemanticError ;
327350
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 {
330353 chains, currency, amount, description, features, absolute_expiry, paths, issuer,
331354 quantity_min, quantity_max, recurrence, node_id, send_invoice, refund_for, signature,
332- } = tlv_stream ;
355+ } , data ) = offer ;
333356
334357 let supported_chains = [
335358 genesis_block ( Network :: Bitcoin ) . block_hash ( ) ,
@@ -394,6 +417,14 @@ impl TryFrom<OfferTlvStream> for Offer {
394417 ( Some ( _) , _) => Some ( SendInvoice { refund_for } ) ,
395418 } ;
396419
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+
397428 Ok ( Offer {
398429 chains,
399430 amount,
@@ -414,7 +445,7 @@ impl TryFrom<OfferTlvStream> for Offer {
414445
415446const OFFER_BECH32_HRP : & str = "lno" ;
416447
417- impl FromStr for OfferTlvStream {
448+ impl FromStr for ParsedOffer {
418449 type Err = ParseError ;
419450
420451 fn from_str ( s : & str ) -> Result < Self , <Self as FromStr >:: Err > {
@@ -434,7 +465,7 @@ impl FromStr for OfferTlvStream {
434465 }
435466
436467 let data = Vec :: < u8 > :: from_base32 ( & data) ?;
437- Ok ( Readable :: read ( & mut & data[ ..] ) ?)
468+ Ok ( ParsedOffer ( Readable :: read ( & mut & data[ ..] ) ?, data ) )
438469 }
439470}
440471
@@ -460,7 +491,7 @@ impl core::fmt::Display for OfferTlvStream {
460491
461492#[ cfg( test) ]
462493mod tests {
463- use super :: { Offer , OfferTlvStream , ParseError } ;
494+ use super :: { Offer , ParseError , ParsedOffer } ;
464495 use bitcoin:: bech32;
465496 use ln:: msgs:: DecodeError ;
466497
@@ -487,7 +518,7 @@ mod tests {
487518 ] ;
488519 for encoded_offer in & offers {
489520 // 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 > ( ) {
491522 panic ! ( "Invalid offer ({:?}): {}" , e, encoded_offer) ;
492523 }
493524 }
0 commit comments