@@ -135,7 +135,7 @@ use crate::offers::merkle::{
135135} ; 
136136use  crate :: offers:: nonce:: Nonce ; 
137137use  crate :: offers:: offer:: { 
138- 	Amount ,  ExperimentalOfferTlvStream ,  ExperimentalOfferTlvStreamRef ,  OfferTlvStream , 
138+ 	Amount ,  ExperimentalOfferTlvStream ,  ExperimentalOfferTlvStreamRef ,  OfferId ,   OfferTlvStream , 
139139	OfferTlvStreamRef ,  Quantity ,  EXPERIMENTAL_OFFER_TYPES ,  OFFER_TYPES , 
140140} ; 
141141use  crate :: offers:: parse:: { Bolt12ParseError ,  Bolt12SemanticError ,  ParsedMessage } ; 
@@ -665,6 +665,19 @@ impl UnsignedBolt12Invoice {
665665 	pub  fn  tagged_hash ( & self )  -> & TaggedHash  { 
666666		& self . tagged_hash 
667667	} 
668+ 
669+ 	/// Computes the [`OfferId`] if this invoice corresponds to an [`Offer`]. 
670+  	/// 
671+  	/// [`Offer`]: crate::offers::offer::Offer 
672+  	#[ inline]  
673+ 	fn  compute_offer_id ( & self )  -> Option < OfferId >  { 
674+ 		match  & self . contents  { 
675+ 			InvoiceContents :: ForOffer  {  .. }  => { 
676+ 				Some ( OfferId :: from_valid_invreq_tlv_stream ( & self . bytes ) ) 
677+ 			} , 
678+ 			InvoiceContents :: ForRefund  {  .. }  => None , 
679+ 		} 
680+ 	} 
668681} 
669682
670683macro_rules!  unsigned_invoice_sign_method {  ( $self:  ident,  $self_type:  ty $( ,  $self_mut:  tt) ?)  => { 
@@ -686,6 +699,9 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
686699		// Append the experimental bytes after the signature. 
687700		$self. bytes. extend_from_slice( & $self. experimental_bytes) ; 
688701
702+ 		// Compute offer_id before moving fields 
703+ 		let  offer_id = $self. compute_offer_id( ) ; 
704+ 
689705		Ok ( Bolt12Invoice  { 
690706			#[ cfg( not( c_bindings) ) ] 
691707			bytes:  $self. bytes, 
@@ -700,6 +716,7 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
700716			tagged_hash:  $self. tagged_hash, 
701717			#[ cfg( c_bindings) ] 
702718			tagged_hash:  $self. tagged_hash. clone( ) , 
719+ 			offer_id, 
703720		} ) 
704721	} 
705722}  } 
@@ -734,6 +751,7 @@ pub struct Bolt12Invoice {
734751	contents :  InvoiceContents , 
735752	signature :  Signature , 
736753	tagged_hash :  TaggedHash , 
754+ 	offer_id :  Option < OfferId > , 
737755} 
738756
739757/// The contents of an [`Bolt12Invoice`] for responding to either an [`Offer`] or a [`Refund`]. 
@@ -967,6 +985,13 @@ impl Bolt12Invoice {
967985		self . tagged_hash . as_digest ( ) . as_ref ( ) . clone ( ) 
968986	} 
969987
988+ 	/// Returns the [`OfferId`] if this invoice corresponds to an [`Offer`]. 
989+  	/// 
990+  	/// [`Offer`]: crate::offers::offer::Offer 
991+  	pub  fn  offer_id ( & self )  -> Option < OfferId >  { 
992+ 		self . offer_id 
993+ 	} 
994+ 
970995	/// Verifies that the invoice was for a request or refund created using the given key by 
971996 	/// checking the payer metadata from the invoice request. 
972997 	/// 
@@ -1622,7 +1647,11 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
16221647		let  pubkey = contents. fields ( ) . signing_pubkey ; 
16231648		merkle:: verify_signature ( & signature,  & tagged_hash,  pubkey) ?; 
16241649
1625- 		Ok ( Bolt12Invoice  {  bytes,  contents,  signature,  tagged_hash } ) 
1650+ 		let  offer_id = match  & contents { 
1651+ 			InvoiceContents :: ForOffer  {  .. }  => Some ( OfferId :: from_valid_invreq_tlv_stream ( & bytes) ) , 
1652+ 			InvoiceContents :: ForRefund  {  .. }  => None , 
1653+ 		} ; 
1654+ 		Ok ( Bolt12Invoice  {  bytes,  contents,  signature,  tagged_hash,  offer_id } ) 
16261655	} 
16271656} 
16281657
@@ -3556,4 +3585,49 @@ mod tests {
35563585			) , 
35573586		} 
35583587	} 
3588+ 
3589+ 	#[ test]  
3590+ 	fn  invoice_offer_id_matches_offer_id ( )  { 
3591+ 		let  expanded_key = ExpandedKey :: new ( [ 42 ;  32 ] ) ; 
3592+ 		let  entropy = FixedEntropy  { } ; 
3593+ 		let  nonce = Nonce :: from_entropy_source ( & entropy) ; 
3594+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
3595+ 		let  payment_id = PaymentId ( [ 1 ;  32 ] ) ; 
3596+ 
3597+ 		let  offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ; 
3598+ 
3599+ 		let  offer_id = offer. id ( ) ; 
3600+ 
3601+ 		let  invoice_request = offer
3602+ 			. request_invoice ( & expanded_key,  nonce,  & secp_ctx,  payment_id) 
3603+ 			. unwrap ( ) 
3604+ 			. build_and_sign ( ) 
3605+ 			. unwrap ( ) ; 
3606+ 
3607+ 		let  invoice = invoice_request
3608+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  now ( ) ) 
3609+ 			. unwrap ( ) 
3610+ 			. build ( ) 
3611+ 			. unwrap ( ) 
3612+ 			. sign ( recipient_sign) 
3613+ 			. unwrap ( ) ; 
3614+ 
3615+ 		assert_eq ! ( invoice. offer_id( ) ,  Some ( offer_id) ) ; 
3616+ 	} 
3617+ 
3618+ 	#[ test]  
3619+ 	fn  refund_invoice_has_no_offer_id ( )  { 
3620+ 		let  refund =
3621+ 			RefundBuilder :: new ( vec ! [ 1 ;  32 ] ,  payer_pubkey ( ) ,  1000 ) . unwrap ( ) . build ( ) . unwrap ( ) ; 
3622+ 
3623+ 		let  invoice = refund
3624+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
3625+ 			. unwrap ( ) 
3626+ 			. build ( ) 
3627+ 			. unwrap ( ) 
3628+ 			. sign ( recipient_sign) 
3629+ 			. unwrap ( ) ; 
3630+ 
3631+ 		assert_eq ! ( invoice. offer_id( ) ,  None ) ; 
3632+ 	} 
35593633} 
0 commit comments