@@ -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