@@ -80,12 +80,14 @@ use core::time::Duration;
8080use  crate :: io; 
8181use  crate :: ln:: PaymentHash ; 
8282use  crate :: ln:: features:: InvoiceRequestFeatures ; 
83+ use  crate :: ln:: inbound_payment:: { ExpandedKey ,  Nonce } ; 
8384use  crate :: ln:: msgs:: { DecodeError ,  MAX_VALUE_MSAT } ; 
8485use  crate :: offers:: invoice:: { BlindedPayInfo ,  InvoiceBuilder } ; 
8586use  crate :: offers:: invoice_request:: { InvoiceRequestTlvStream ,  InvoiceRequestTlvStreamRef } ; 
8687use  crate :: offers:: offer:: { OfferTlvStream ,  OfferTlvStreamRef } ; 
8788use  crate :: offers:: parse:: { Bech32Encode ,  ParseError ,  ParsedMessage ,  SemanticError } ; 
8889use  crate :: offers:: payer:: { PayerContents ,  PayerTlvStream ,  PayerTlvStreamRef } ; 
90+ use  crate :: offers:: signer:: { MetadataMaterial ,  DerivedPubkey } ; 
8991use  crate :: onion_message:: BlindedPath ; 
9092use  crate :: util:: ser:: { SeekReadable ,  WithoutLength ,  Writeable ,  Writer } ; 
9193use  crate :: util:: string:: PrintableString ; 
@@ -102,6 +104,7 @@ use std::time::SystemTime;
102104/// [module-level documentation]: self 
103105pub  struct  RefundBuilder  { 
104106	refund :  RefundContents , 
107+ 	metadata_material :  Option < MetadataMaterial > , 
105108} 
106109
107110impl  RefundBuilder  { 
@@ -123,7 +126,53 @@ impl RefundBuilder {
123126			quantity :  None ,  payer_id,  payer_note :  None , 
124127		} ; 
125128
126- 		Ok ( RefundBuilder  {  refund } ) 
129+ 		Ok ( RefundBuilder  {  refund,  metadata_material :  None  } ) 
130+ 	} 
131+ 
132+ 	/// Similar to [`RefundBuilder::new`] except it: 
133+ /// - derives the payer id such that a different key can be used for each refund, and 
134+ /// - sets the metadata when [`RefundBuilder::build`] is called such that it can be used by 
135+ ///   [`Invoice::verify`] to determine if the refund was produced using a base [`ExpandedKey`] 
136+ ///   from which the payer id was derived. 
137+ /// 
138+ /// [`Invoice::verify`]: crate::offers::invoice::Invoice::verify 
139+ /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey 
140+ #[ allow( unused) ]  
141+ 	pub ( crate )  fn  deriving_payer_id ( 
142+ 		description :  String ,  payer_id :  DerivedPubkey ,  amount_msats :  u64 
143+ 	)  -> Result < Self ,  SemanticError >  { 
144+ 		if  amount_msats > MAX_VALUE_MSAT  { 
145+ 			return  Err ( SemanticError :: InvalidAmount ) ; 
146+ 		} 
147+ 
148+ 		let  ( payer_id,  metadata_material)  = payer_id. into_parts ( ) ; 
149+ 		let  refund = RefundContents  { 
150+ 			payer :  PayerContents ( vec ! [ ] ) ,  description,  absolute_expiry :  None ,  issuer :  None , 
151+ 			paths :  None ,  chain :  None ,  amount_msats,  features :  InvoiceRequestFeatures :: empty ( ) , 
152+ 			quantity :  None ,  payer_id,  payer_note :  None , 
153+ 		} ; 
154+ 
155+ 		Ok ( RefundBuilder  {  refund,  metadata_material :  Some ( metadata_material)  } ) 
156+ 	} 
157+ 
158+ 	/// Sets the [`Refund::metadata`] derived from the given `key` and any fields set prior to 
159+ /// calling [`Refund::build`]. Allows for stateless verification of an [`Invoice`] when using a 
160+ /// public node id as the [`Refund::payer_id`] instead of a derived one. 
161+ /// 
162+ /// Errors if already called or if the builder was constructed with [`Self::deriving_payer_id`]. 
163+ /// 
164+ /// [`Invoice`]: crate::offers::invoice::Invoice 
165+ #[ allow( unused) ]  
166+ 	pub ( crate )  fn  metadata_derived ( 
167+ 		mut  self ,  key :  & ExpandedKey ,  nonce :  Nonce 
168+ 	)  -> Result < Self ,  SemanticError >  { 
169+ 		if  self . metadata_material . is_some ( )  { 
170+ 			return  Err ( SemanticError :: UnexpectedMetadata ) ; 
171+ 		} 
172+ 
173+ 		self . refund . payer  = PayerContents ( vec ! [ ] ) ; 
174+ 		self . metadata_material  = Some ( MetadataMaterial :: new ( nonce,  key) ) ; 
175+ 		Ok ( self ) 
127176	} 
128177
129178	/// Sets the [`Refund::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has 
@@ -190,6 +239,17 @@ impl RefundBuilder {
190239			self . refund . chain  = None ; 
191240		} 
192241
242+ 		// Create the metadata for stateless verification of an Invoice. 
243+ 		if  let  Some ( mut  metadata_material)  = self . metadata_material  { 
244+ 			debug_assert ! ( self . refund. payer. 0 . is_empty( ) ) ; 
245+ 			let  mut  tlv_stream = self . refund . as_tlv_stream ( ) ; 
246+ 			tlv_stream. 0 . metadata  = None ; 
247+ 			tlv_stream. 2 . payer_id  = None ; 
248+ 			tlv_stream. write ( & mut  metadata_material) . unwrap ( ) ; 
249+ 
250+ 			self . refund . payer . 0  = metadata_material. into_metadata ( ) ; 
251+ 		} 
252+ 
193253		let  mut  bytes = Vec :: new ( ) ; 
194254		self . refund . write ( & mut  bytes) . unwrap ( ) ; 
195255
0 commit comments