@@ -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 } ; 
@@ -686,6 +686,13 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
686686		// Append the experimental bytes after the signature. 
687687		$self. bytes. extend_from_slice( & $self. experimental_bytes) ; 
688688
689+ 		let  offer_id = match  & $self. contents { 
690+ 			InvoiceContents :: ForOffer  {  .. }  => { 
691+ 				Some ( OfferId :: from_valid_bolt12_tlv_stream( & $self. bytes) ) 
692+ 			} , 
693+ 			InvoiceContents :: ForRefund  {  .. }  => None , 
694+ 		} ; 
695+ 
689696		Ok ( Bolt12Invoice  { 
690697			#[ cfg( not( c_bindings) ) ] 
691698			bytes:  $self. bytes, 
@@ -700,6 +707,7 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
700707			tagged_hash:  $self. tagged_hash, 
701708			#[ cfg( c_bindings) ] 
702709			tagged_hash:  $self. tagged_hash. clone( ) , 
710+ 			offer_id, 
703711		} ) 
704712	} 
705713}  } 
@@ -734,6 +742,7 @@ pub struct Bolt12Invoice {
734742	contents :  InvoiceContents , 
735743	signature :  Signature , 
736744	tagged_hash :  TaggedHash , 
745+ 	offer_id :  Option < OfferId > , 
737746} 
738747
739748/// The contents of an [`Bolt12Invoice`] for responding to either an [`Offer`] or a [`Refund`]. 
@@ -967,6 +976,13 @@ impl Bolt12Invoice {
967976		self . tagged_hash . as_digest ( ) . as_ref ( ) . clone ( ) 
968977	} 
969978
979+ 	/// Returns the [`OfferId`] if this invoice corresponds to an [`Offer`]. 
980+  	/// 
981+  	/// [`Offer`]: crate::offers::offer::Offer 
982+  	pub  fn  offer_id ( & self )  -> Option < OfferId >  { 
983+ 		self . offer_id 
984+ 	} 
985+ 
970986	/// Verifies that the invoice was for a request or refund created using the given key by 
971987 	/// checking the payer metadata from the invoice request. 
972988 	/// 
@@ -1032,6 +1048,11 @@ impl Bolt12Invoice {
10321048			InvoiceContents :: ForRefund  {  .. }  => self . message_paths ( ) . is_empty ( ) , 
10331049		} 
10341050	} 
1051+ 
1052+ 	/// Returns the [`TaggedHash`] of the invoice that was signed. 
1053+  	pub  fn  tagged_hash ( & self )  -> & TaggedHash  { 
1054+ 		& self . tagged_hash 
1055+ 	} 
10351056} 
10361057
10371058impl  PartialEq  for  Bolt12Invoice  { 
@@ -1626,7 +1647,11 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
16261647		let  pubkey = contents. fields ( ) . signing_pubkey ; 
16271648		merkle:: verify_signature ( & signature,  & tagged_hash,  pubkey) ?; 
16281649
1629- 		Ok ( Bolt12Invoice  {  bytes,  contents,  signature,  tagged_hash } ) 
1650+ 		let  offer_id = match  & contents { 
1651+ 			InvoiceContents :: ForOffer  {  .. }  => Some ( OfferId :: from_valid_bolt12_tlv_stream ( & bytes) ) , 
1652+ 			InvoiceContents :: ForRefund  {  .. }  => None , 
1653+ 		} ; 
1654+ 		Ok ( Bolt12Invoice  {  bytes,  contents,  signature,  tagged_hash,  offer_id } ) 
16301655	} 
16311656} 
16321657
@@ -1785,7 +1810,6 @@ mod tests {
17851810	use  bitcoin:: script:: ScriptBuf ; 
17861811	use  bitcoin:: secp256k1:: { self ,  Keypair ,  Message ,  Secp256k1 ,  SecretKey ,  XOnlyPublicKey } ; 
17871812	use  bitcoin:: { CompressedPublicKey ,  WitnessProgram ,  WitnessVersion } ; 
1788- 
17891813	use  core:: time:: Duration ; 
17901814
17911815	use  crate :: blinded_path:: message:: BlindedMessagePath ; 
@@ -3560,4 +3584,85 @@ mod tests {
35603584			) , 
35613585		} 
35623586	} 
3587+ 
3588+ 	#[ test]  
3589+ 	fn  invoice_offer_id_matches_offer_id ( )  { 
3590+ 		let  expanded_key = ExpandedKey :: new ( [ 42 ;  32 ] ) ; 
3591+ 		let  entropy = FixedEntropy  { } ; 
3592+ 		let  nonce = Nonce :: from_entropy_source ( & entropy) ; 
3593+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
3594+ 		let  payment_id = PaymentId ( [ 1 ;  32 ] ) ; 
3595+ 
3596+ 		let  offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ; 
3597+ 
3598+ 		let  offer_id = offer. id ( ) ; 
3599+ 
3600+ 		let  invoice_request = offer
3601+ 			. request_invoice ( & expanded_key,  nonce,  & secp_ctx,  payment_id) 
3602+ 			. unwrap ( ) 
3603+ 			. build_and_sign ( ) 
3604+ 			. unwrap ( ) ; 
3605+ 
3606+ 		let  invoice = invoice_request
3607+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  now ( ) ) 
3608+ 			. unwrap ( ) 
3609+ 			. build ( ) 
3610+ 			. unwrap ( ) 
3611+ 			. sign ( recipient_sign) 
3612+ 			. unwrap ( ) ; 
3613+ 
3614+ 		assert_eq ! ( invoice. offer_id( ) ,  Some ( offer_id) ) ; 
3615+ 	} 
3616+ 
3617+ 	#[ test]  
3618+ 	fn  refund_invoice_has_no_offer_id ( )  { 
3619+ 		let  refund =
3620+ 			RefundBuilder :: new ( vec ! [ 1 ;  32 ] ,  payer_pubkey ( ) ,  1000 ) . unwrap ( ) . build ( ) . unwrap ( ) ; 
3621+ 
3622+ 		let  invoice = refund
3623+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
3624+ 			. unwrap ( ) 
3625+ 			. build ( ) 
3626+ 			. unwrap ( ) 
3627+ 			. sign ( recipient_sign) 
3628+ 			. unwrap ( ) ; 
3629+ 
3630+ 		assert_eq ! ( invoice. offer_id( ) ,  None ) ; 
3631+ 	} 
3632+ 
3633+ 	#[ test]  
3634+ 	fn  verifies_invoice_signature_with_tagged_hash ( )  { 
3635+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
3636+ 		let  expanded_key = ExpandedKey :: new ( [ 42 ;  32 ] ) ; 
3637+ 		let  entropy = FixedEntropy  { } ; 
3638+ 		let  nonce = Nonce :: from_entropy_source ( & entropy) ; 
3639+ 		let  node_id = recipient_pubkey ( ) ; 
3640+ 		let  payment_paths = payment_paths ( ) ; 
3641+ 		let  now = Duration :: from_secs ( 123456 ) ; 
3642+ 		let  payment_id = PaymentId ( [ 1 ;  32 ] ) ; 
3643+ 
3644+ 		let  offer = OfferBuilder :: new ( node_id) 
3645+ 			. amount_msats ( 1000 ) 
3646+ 			. build ( ) 
3647+ 			. unwrap ( ) ; 
3648+ 
3649+ 		let  invoice_request = offer
3650+ 			. request_invoice ( & expanded_key,  nonce,  & secp_ctx,  payment_id) 
3651+ 			. unwrap ( ) 
3652+ 			. build_and_sign ( ) 
3653+ 			. unwrap ( ) ; 
3654+ 
3655+ 		let  invoice = invoice_request
3656+ 			. respond_with_no_std ( payment_paths,  payment_hash ( ) ,  now) 
3657+ 			. unwrap ( ) 
3658+ 			. build ( ) 
3659+ 			. unwrap ( ) 
3660+ 			. sign ( recipient_sign) 
3661+ 			. unwrap ( ) ; 
3662+ 
3663+ 		let  issuer_sign_pubkey = offer. issuer_signing_pubkey ( ) . unwrap ( ) ; 
3664+ 		let  tagged_hash = invoice. tagged_hash ( ) ; 
3665+ 		let  signature = invoice. signature ( ) ; 
3666+ 		assert ! ( merkle:: verify_signature( & signature,  tagged_hash,  issuer_sign_pubkey) . is_ok( ) ) ; 
3667+ 	} 
35633668} 
0 commit comments